package com.example.dattingapp import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import com.example.dattingapp.ui.theme.DattingAPPTheme import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Favorite import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import android.util.Log import androidx.compose.foundation.clickable import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.filled.FavoriteBorder import androidx.compose.material.icons.outlined.ChatBubbleOutline import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.example.dattingapp.dataclass.Author import com.example.dattingapp.dataclass.ShowPostResponse import com.example.dattingapp.homes.networkandapi.ShowPostUiState import com.example.dattingapp.homes.networkandapi.ShowPostViewModel import androidx.compose.material.icons.filled.Send import androidx.compose.material3.CardDefaults import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.example.dattingapp.dataclass.CommentResponse import com.example.dattingapp.dataclass.Location import com.example.dattingapp.homes.networkandapi.TokenManager import com.example.dattingapp.ui.theme.TextPostTextColor val AccentPink = Color(0xFFD81B60) val CommentBubbleBg = Color(0xFFF6F6F6) class ShowPostingActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() val postId = intent.getIntExtra("Post_ID", 0) Log.d("Post_ID", "$postId") setContent { DattingAPPTheme { val context = LocalContext.current val showPostViewModel: ShowPostViewModel = viewModel() val uiState by showPostViewModel.showPostUiState.collectAsState() val comments by showPostViewModel.commentsUiState.collectAsState() // Trigger API calls when the screen launches LaunchedEffect(postId) { showPostViewModel.fetchPostById(context, postId) showPostViewModel.fetchComments(context, postId) } Scaffold( modifier = Modifier.fillMaxSize() ) { innerPadding -> Box( modifier = Modifier .fillMaxSize() .padding(innerPadding), ) { when (val state = uiState) { is ShowPostUiState.Idle, is ShowPostUiState.Loading -> { CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } is ShowPostUiState.Success -> { // Full screen scrollable container Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(bottom = 24.dp) ) { PostCard( post = state.post, comments = comments, ) } } is ShowPostUiState.Error -> { Text( text = state.message, color = Color.Red, modifier = Modifier.align(Alignment.Center) ) } } } } } } } } @Composable fun PostCard( post: ShowPostResponse, comments: List, modifier: Modifier = Modifier ) { Column( modifier = modifier .fillMaxWidth() .padding(vertical = 8.dp) ) { // --- 1. Author Header --- Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically ) { AsyncImage( model = post.author?.profile_image_url, contentDescription = "Post author image", placeholder = painterResource(id = R.drawable.locationpermissionbg), // your existing placeholder error = painterResource(id = R.drawable.locationpermissionbg), modifier = Modifier .size(40.dp) .clip(CircleShape), contentScale = ContentScale.Crop ) Spacer(modifier = Modifier.width(8.dp)) Column { Row(verticalAlignment = Alignment.CenterVertically) { val authorName = post.author?.full_name ?: "Anonymous" val feelingText = post.feeling ?: "" val context = LocalContext.current // ✅ Clickable author name Text( text = authorName, fontSize = 16.sp, fontWeight = FontWeight.Bold, modifier = Modifier.clickable { val intent = Intent(context, FriendProfile::class.java).apply { putExtra("USER_ID", post.author?.id) // ← send the user id } context.startActivity(intent) } ) // Rest of the line, unchanged if (feelingText.isNotEmpty()) { Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Normal)) { append(" — feeling ") } withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { append(feelingText) } }, fontSize = 16.sp ) } } if (!post.location?.name.isNullOrEmpty()) { Text( text = post.location?.name ?: "", style = MaterialTheme.typography.bodySmall, color = Color.Gray ) } } } // --- 2. Post Text Content --- // --- 3. Media Carousel (Images) --- if (post.media.isNotEmpty()) { if (!post.content.isNullOrEmpty()) { Text( text = post.content, style = MaterialTheme.typography.bodyMedium, modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp, vertical = 4.dp) ) } Card( shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() .padding(start = 10.dp, end = 10.dp) .height(360.dp), ) { AsyncImage( model = post.media.first().url, contentDescription = "Post Media Image", contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize() ) } }else{ Card( shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() .padding(start = 10.dp, end = 10.dp) .height(360.dp), colors = CardDefaults.cardColors( containerColor = Color.White // your background color ) ) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text( text = post.content.orEmpty(), color = TextPostTextColor, fontSize = if ((post.content?.length ?: 0) > 120) 18.sp else 24.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, maxLines = 8, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(horizontal = 32.dp) ) } } } // --- 4. Interaction Buttons --- Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 4.dp), verticalAlignment = Alignment.CenterVertically ) { IconButton(onClick = { /* Handle like action */ }) { Icon( imageVector = if (post.liked_by_me) Icons.Filled.Favorite else Icons.Default.FavoriteBorder, contentDescription = "Like", tint = if (post.liked_by_me) Color.Red else Color.Black ) } Text(text = "${post.like_count}") Spacer(modifier = Modifier.width(16.dp)) IconButton(onClick = { /* Handle comments */ }) { Icon( imageVector = Icons.Outlined.ChatBubbleOutline, contentDescription = "Comments" ) } Text(text = "${comments.size}") } // --- 5. Comment Section UI --- CommentSection( comments = comments, postId = post.id, postUserName = post.author?.full_name // see note below ) } } @Composable fun CommentSection( comments: List, postId: Int, postUserName: String?, postUserId: Int? = null, viewModel: ShowPostViewModel = viewModel() ) { val context = LocalContext.current var newCommentText by remember { mutableStateOf("") } var menuCommentId by remember { mutableStateOf(null) } var editingCommentId by remember { mutableStateOf(null) } var editText by remember { mutableStateOf("") } val tokenManager = remember { TokenManager.getInstance(context) } val currentUserName = remember { tokenManager.currentUserName() } val currentProfileImage = remember { tokenManager.currentProfileImage() } // ✅ get the logged-in user's id once val currentUserId = remember { tokenManager.getUserId() } Column( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp, vertical = 8.dp) ) { Text( text = "Comments", style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.Bold, modifier = Modifier.padding(bottom = 8.dp) ) // Comment input bar Row( modifier = Modifier .fillMaxWidth() .background(CommentBubbleBg, shape = RoundedCornerShape(24.dp)) .padding(horizontal = 12.dp, vertical = 4.dp), verticalAlignment = Alignment.CenterVertically ) { BasicTextField( value = newCommentText, onValueChange = { newCommentText = it }, modifier = Modifier .weight(1f) .padding(vertical = 8.dp), decorationBox = { innerTextField -> if (newCommentText.isEmpty()) { Text( text = "Add a comment...", color = Color.Gray, style = MaterialTheme.typography.bodyMedium ) } innerTextField() } ) IconButton( onClick = { if (newCommentText.isNotBlank()) { viewModel.addCommentLocallyAndServer( context = context, postId = postId, content = newCommentText, currentUserName = currentUserName ) newCommentText = "" } }, modifier = Modifier.size(32.dp) ) { Icon( imageVector = Icons.Default.Send, contentDescription = "Send Comment", tint = AccentPink, modifier = Modifier.size(16.dp) ) } } // List of comments comments.reversed().forEach { comment -> val isEditing = editingCommentId == comment.id // ✅ Permission checks per comment // adjust `comment.author?.id` to your actual model field (e.g. comment.user_id) val commentAuthorId = comment.author?.id val isCommentOwner = commentAuthorId != null && commentAuthorId == currentUserId val isPostOwner = postUserId != null && postUserId == currentUserId val canEdit = isCommentOwner val canDelete = isCommentOwner || isPostOwner val hasAnyAction = canEdit || canDelete Log.d( "CommentPerm", "current=$currentUserId | commentAuthor=$commentAuthorId | postOwner=$postUserId | canEdit=$canEdit canDelete=$canDelete" ) Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 6.dp), verticalAlignment = Alignment.CenterVertically ) { AsyncImage( model = currentProfileImage, contentDescription = "Post author image", placeholder = painterResource(id = R.drawable.locationpermissionbg), // your existing placeholder error = painterResource(id = R.drawable.locationpermissionbg), modifier = Modifier .size(32.dp) .clip(CircleShape), contentScale = ContentScale.Crop ) Spacer(modifier = Modifier.width(8.dp)) Box(modifier = Modifier.weight(1f)) { Column( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(12.dp)) .background(CommentBubbleBg) // ✅ only open the menu if the user can actually do something .clickable(enabled = !isEditing && hasAnyAction) { menuCommentId = comment.id } .padding(horizontal = 12.dp, vertical = 8.dp) ) { Text( text = comment.author?.full_name ?: "Anonymous", style = MaterialTheme.typography.bodySmall, fontWeight = FontWeight.Bold ) Spacer(modifier = Modifier.height(2.dp)) if (isEditing) { BasicTextField( value = editText, onValueChange = { editText = it }, modifier = Modifier.fillMaxWidth(), textStyle = MaterialTheme.typography.bodyMedium ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End ) { TextButton(onClick = { editingCommentId = null editText = "" }) { Text("Cancel") } TextButton(onClick = { if (editText.isNotBlank()) { // viewModel.editComment( // context = context, // postId = postId, // commentId = comment.id, // newContent = editText // ) editingCommentId = null editText = "" } }) { Text("Save", color = AccentPink) } } } else { Text( text = comment.content, style = MaterialTheme.typography.bodyMedium ) } } DropdownMenu( expanded = menuCommentId == comment.id, onDismissRequest = { menuCommentId = null } ) { // ✅ Edit: only the comment author if (canEdit) { DropdownMenuItem( text = { Text("Edit") }, onClick = { editText = comment.content editingCommentId = comment.id menuCommentId = null } ) } // ✅ Delete: comment author OR post owner if (canDelete) { DropdownMenuItem( text = { Text("Delete") }, onClick = { menuCommentId = null viewModel.deleteComment( context = context, postId = postId, commentId = comment.id ) } ) } } } } } Spacer(modifier = Modifier.height(8.dp)) } } @Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) @Composable fun ShowPostScreenPreview() { DattingAPPTheme { // Mock sample data mirroring your API schema val mockPost = ShowPostResponse( id = 21, content = "Exploring the city and enjoying some coffee! ☕🏙️ #Android #JetpackCompose", postType = "image", contentFormat = "markdown", feeling = "happy 😊", location = Location( name = "Chiattagong", latitude = 27.9671, longitude = -175.0599 ), author = Author( id = 49, full_name = "Alex Rivera", city = "Shanghai", country = "China", profile_image_url=null, compatibilityPercentage = 95 ), like_count = 12, comment_count = 2, liked_by_me = true, media = listOf( com.example.dattingapp.dataclass.MediaItem( id = 5, mediaType = "image", mimeType = "image/png", sizeBytes = 199619, position = 0, url = "https://images.unsplash.com/photo-1541167760496-1628856ab772" ) ), createdAt = "2026-10-13T06:06:15" ) // Mock comments list val mockComments = listOf( CommentResponse( id = 1, postId = 21, content = "This spot looks amazing! Enjoy your day! ☕", author = Author( id = 54, full_name = "Sarah", city = "Shanghai", country = "China", profile_image_url=null, compatibilityPercentage = 100 ), createdAt = "2026-10-13T07:45:15" ), CommentResponse( id = 2, postId = 21, content = "What a wonderful view, I love it!", author = Author( id = 55, full_name = "David", city = "Beijing", country = "China", profile_image_url=null, compatibilityPercentage = 90 ), createdAt = "2026-10-13T08:04:48" ) ) // Full screen container matching activity layout Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(vertical = 16.dp) ) { PostCard( post = mockPost, comments = mockComments, ) } } }