|
|
@ -1,57 +1,57 @@ |
|
|
package com.example.dattingapp |
|
|
package com.example.dattingapp |
|
|
|
|
|
|
|
|
|
|
|
import android.app.Activity |
|
|
|
|
|
import android.content.Context |
|
|
import android.content.Intent |
|
|
import android.content.Intent |
|
|
import android.os.Bundle |
|
|
import android.os.Bundle |
|
|
|
|
|
import android.util.Log |
|
|
import androidx.activity.ComponentActivity |
|
|
import androidx.activity.ComponentActivity |
|
|
import androidx.activity.compose.setContent |
|
|
import androidx.activity.compose.setContent |
|
|
import androidx.activity.enableEdgeToEdge |
|
|
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.background |
|
|
|
|
|
import androidx.compose.foundation.clickable |
|
|
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.text.BasicTextField |
|
|
|
|
|
import androidx.compose.foundation.verticalScroll |
|
|
import androidx.compose.material.icons.Icons |
|
|
import androidx.compose.material.icons.Icons |
|
|
import androidx.compose.material.icons.filled.Favorite |
|
|
import androidx.compose.material.icons.filled.Favorite |
|
|
|
|
|
import androidx.compose.material.icons.filled.FavoriteBorder |
|
|
|
|
|
import androidx.compose.material.icons.filled.MoreVert |
|
|
|
|
|
import androidx.compose.material.icons.filled.Send |
|
|
|
|
|
import androidx.compose.material.icons.outlined.ChatBubbleOutline |
|
|
import androidx.compose.material3.* |
|
|
import androidx.compose.material3.* |
|
|
import androidx.compose.runtime.* |
|
|
import androidx.compose.runtime.* |
|
|
import androidx.compose.ui.Alignment |
|
|
import androidx.compose.ui.Alignment |
|
|
|
|
|
import androidx.compose.ui.Modifier |
|
|
import androidx.compose.ui.draw.clip |
|
|
import androidx.compose.ui.draw.clip |
|
|
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.platform.LocalContext |
|
|
import androidx.compose.ui.unit.dp |
|
|
import androidx.compose.ui.res.painterResource |
|
|
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.SpanStyle |
|
|
import androidx.compose.ui.text.buildAnnotatedString |
|
|
import androidx.compose.ui.text.buildAnnotatedString |
|
|
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.TextOverflow |
|
|
import androidx.compose.ui.text.withStyle |
|
|
import androidx.compose.ui.text.withStyle |
|
|
|
|
|
import androidx.compose.ui.tooling.preview.Preview |
|
|
|
|
|
import androidx.compose.ui.unit.dp |
|
|
import androidx.compose.ui.unit.sp |
|
|
import androidx.compose.ui.unit.sp |
|
|
import androidx.lifecycle.viewmodel.compose.viewModel |
|
|
import androidx.lifecycle.viewmodel.compose.viewModel |
|
|
|
|
|
import coil.compose.AsyncImage |
|
|
import com.example.dattingapp.dataclass.Author |
|
|
import com.example.dattingapp.dataclass.Author |
|
|
|
|
|
import com.example.dattingapp.dataclass.CommentResponse |
|
|
|
|
|
import com.example.dattingapp.dataclass.Location |
|
|
import com.example.dattingapp.dataclass.ShowPostResponse |
|
|
import com.example.dattingapp.dataclass.ShowPostResponse |
|
|
|
|
|
import com.example.dattingapp.homes.networkandapi.PostAllLikerResponse |
|
|
import com.example.dattingapp.homes.networkandapi.ShowPostUiState |
|
|
import com.example.dattingapp.homes.networkandapi.ShowPostUiState |
|
|
import com.example.dattingapp.homes.networkandapi.ShowPostViewModel |
|
|
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.homes.networkandapi.TokenManager |
|
|
|
|
|
import com.example.dattingapp.homes.networkandapi.rememberPostInteraction |
|
|
|
|
|
import com.example.dattingapp.ui.theme.DattingAPPTheme |
|
|
import com.example.dattingapp.ui.theme.TextPostTextColor |
|
|
import com.example.dattingapp.ui.theme.TextPostTextColor |
|
|
|
|
|
|
|
|
val AccentPink = Color(0xFFD81B60) |
|
|
val AccentPink = Color(0xFFD81B60) |
|
|
@ -71,11 +71,12 @@ class ShowPostingActivity : ComponentActivity() { |
|
|
val showPostViewModel: ShowPostViewModel = viewModel() |
|
|
val showPostViewModel: ShowPostViewModel = viewModel() |
|
|
val uiState by showPostViewModel.showPostUiState.collectAsState() |
|
|
val uiState by showPostViewModel.showPostUiState.collectAsState() |
|
|
val comments by showPostViewModel.commentsUiState.collectAsState() |
|
|
val comments by showPostViewModel.commentsUiState.collectAsState() |
|
|
|
|
|
val postLikers by showPostViewModel.postLikers.collectAsState() |
|
|
|
|
|
|
|
|
// Trigger API calls when the screen launches |
|
|
|
|
|
LaunchedEffect(postId) { |
|
|
LaunchedEffect(postId) { |
|
|
showPostViewModel.fetchPostById(context, postId) |
|
|
showPostViewModel.fetchPostById(context, postId) |
|
|
showPostViewModel.fetchComments(context, postId) |
|
|
showPostViewModel.fetchComments(context, postId) |
|
|
|
|
|
showPostViewModel.fetchPostLikers(context, postId) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Scaffold( |
|
|
Scaffold( |
|
|
@ -91,7 +92,6 @@ class ShowPostingActivity : ComponentActivity() { |
|
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) |
|
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) |
|
|
} |
|
|
} |
|
|
is ShowPostUiState.Success -> { |
|
|
is ShowPostUiState.Success -> { |
|
|
// Full screen scrollable container |
|
|
|
|
|
Column( |
|
|
Column( |
|
|
modifier = Modifier |
|
|
modifier = Modifier |
|
|
.fillMaxSize() |
|
|
.fillMaxSize() |
|
|
@ -99,8 +99,11 @@ class ShowPostingActivity : ComponentActivity() { |
|
|
.padding(bottom = 24.dp) |
|
|
.padding(bottom = 24.dp) |
|
|
) { |
|
|
) { |
|
|
PostCard( |
|
|
PostCard( |
|
|
|
|
|
context = context, |
|
|
|
|
|
showPostViewModel = showPostViewModel, |
|
|
post = state.post, |
|
|
post = state.post, |
|
|
comments = comments, |
|
|
comments = comments, |
|
|
|
|
|
postLikers = postLikers |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -121,10 +124,26 @@ class ShowPostingActivity : ComponentActivity() { |
|
|
|
|
|
|
|
|
@Composable |
|
|
@Composable |
|
|
fun PostCard( |
|
|
fun PostCard( |
|
|
|
|
|
context: Context, |
|
|
|
|
|
showPostViewModel: ShowPostViewModel, |
|
|
post: ShowPostResponse, |
|
|
post: ShowPostResponse, |
|
|
comments: List<CommentResponse>, |
|
|
comments: List<CommentResponse>, |
|
|
|
|
|
postLikers: List<PostAllLikerResponse>, |
|
|
modifier: Modifier = Modifier |
|
|
modifier: Modifier = Modifier |
|
|
) { |
|
|
) { |
|
|
|
|
|
// Live like/comment values shared with the feed screen |
|
|
|
|
|
val live = rememberPostInteraction(post.id) |
|
|
|
|
|
val likedByMe = live?.likedByMe ?: post.liked_by_me |
|
|
|
|
|
val likeCount = live?.likeCount ?: post.like_count |
|
|
|
|
|
val commentCount = live?.commentCount ?: comments.size |
|
|
|
|
|
|
|
|
|
|
|
// Owner check + menu state |
|
|
|
|
|
val tokenManager = remember { TokenManager.getInstance(context) } |
|
|
|
|
|
val currentUserId = remember { tokenManager.getUserId() } |
|
|
|
|
|
val isPostOwner = post.author?.id != null && post.author?.id == currentUserId |
|
|
|
|
|
var showPostMenu by remember { mutableStateOf(false) } |
|
|
|
|
|
var showDeleteDialog by remember { mutableStateOf(false) } |
|
|
|
|
|
|
|
|
Column( |
|
|
Column( |
|
|
modifier = modifier |
|
|
modifier = modifier |
|
|
.fillMaxWidth() |
|
|
.fillMaxWidth() |
|
|
@ -140,7 +159,7 @@ fun PostCard( |
|
|
AsyncImage( |
|
|
AsyncImage( |
|
|
model = post.author?.profile_image_url, |
|
|
model = post.author?.profile_image_url, |
|
|
contentDescription = "Post author image", |
|
|
contentDescription = "Post author image", |
|
|
placeholder = painterResource(id = R.drawable.locationpermissionbg), // your existing placeholder |
|
|
placeholder = painterResource(id = R.drawable.locationpermissionbg), |
|
|
error = painterResource(id = R.drawable.locationpermissionbg), |
|
|
error = painterResource(id = R.drawable.locationpermissionbg), |
|
|
modifier = Modifier |
|
|
modifier = Modifier |
|
|
.size(40.dp) |
|
|
.size(40.dp) |
|
|
@ -149,26 +168,24 @@ fun PostCard( |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
Spacer(modifier = Modifier.width(8.dp)) |
|
|
Spacer(modifier = Modifier.width(8.dp)) |
|
|
Column { |
|
|
|
|
|
|
|
|
Column(modifier = Modifier.weight(1f)) { |
|
|
Row(verticalAlignment = Alignment.CenterVertically) { |
|
|
Row(verticalAlignment = Alignment.CenterVertically) { |
|
|
val authorName = post.author?.full_name ?: "Anonymous" |
|
|
val authorName = post.author?.full_name ?: "Anonymous" |
|
|
val feelingText = post.feeling ?: "" |
|
|
val feelingText = post.feeling ?: "" |
|
|
val context = LocalContext.current |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Clickable author name |
|
|
|
|
|
Text( |
|
|
Text( |
|
|
text = authorName, |
|
|
text = authorName, |
|
|
fontSize = 16.sp, |
|
|
fontSize = 16.sp, |
|
|
fontWeight = FontWeight.Bold, |
|
|
fontWeight = FontWeight.Bold, |
|
|
modifier = Modifier.clickable { |
|
|
modifier = Modifier.clickable { |
|
|
val intent = Intent(context, FriendProfile::class.java).apply { |
|
|
val intent = Intent(context, FriendProfile::class.java).apply { |
|
|
putExtra("USER_ID", post.author?.id) // ← send the user id |
|
|
putExtra("USER_ID", post.author?.id) |
|
|
} |
|
|
} |
|
|
context.startActivity(intent) |
|
|
context.startActivity(intent) |
|
|
} |
|
|
} |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
// Rest of the line, unchanged |
|
|
|
|
|
if (feelingText.isNotEmpty()) { |
|
|
if (feelingText.isNotEmpty()) { |
|
|
Text( |
|
|
Text( |
|
|
text = buildAnnotatedString { |
|
|
text = buildAnnotatedString { |
|
|
@ -192,12 +209,40 @@ fun PostCard( |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --- 2. Post Text Content --- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Three-dot menu: only for the post owner |
|
|
|
|
|
if (isPostOwner) { |
|
|
|
|
|
Box { |
|
|
|
|
|
IconButton(onClick = { showPostMenu = true }) { |
|
|
|
|
|
Icon( |
|
|
|
|
|
imageVector = Icons.Default.MoreVert, |
|
|
|
|
|
contentDescription = "Post options" |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
DropdownMenu( |
|
|
|
|
|
expanded = showPostMenu, |
|
|
|
|
|
onDismissRequest = { showPostMenu = false } |
|
|
|
|
|
) { |
|
|
|
|
|
DropdownMenuItem( |
|
|
|
|
|
text = { Text("Edit") }, |
|
|
|
|
|
onClick = { |
|
|
|
|
|
showPostMenu = false |
|
|
|
|
|
// TODO: edit post (empty for now) |
|
|
|
|
|
} |
|
|
|
|
|
) |
|
|
|
|
|
DropdownMenuItem( |
|
|
|
|
|
text = { Text("Delete", color = Color.Red) }, |
|
|
|
|
|
onClick = { |
|
|
|
|
|
showPostMenu = false |
|
|
|
|
|
showDeleteDialog = true |
|
|
|
|
|
} |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// --- 3. Media Carousel (Images) --- |
|
|
// --- 2. Media / text content --- |
|
|
if (post.media.isNotEmpty()) { |
|
|
if (post.media.isNotEmpty()) { |
|
|
|
|
|
|
|
|
if (!post.content.isNullOrEmpty()) { |
|
|
if (!post.content.isNullOrEmpty()) { |
|
|
@ -215,7 +260,6 @@ fun PostCard( |
|
|
.fillMaxWidth() |
|
|
.fillMaxWidth() |
|
|
.padding(start = 10.dp, end = 10.dp) |
|
|
.padding(start = 10.dp, end = 10.dp) |
|
|
.height(360.dp), |
|
|
.height(360.dp), |
|
|
|
|
|
|
|
|
) { |
|
|
) { |
|
|
AsyncImage( |
|
|
AsyncImage( |
|
|
model = post.media.first().url, |
|
|
model = post.media.first().url, |
|
|
@ -225,7 +269,7 @@ fun PostCard( |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
}else{ |
|
|
} else { |
|
|
|
|
|
|
|
|
Card( |
|
|
Card( |
|
|
shape = RoundedCornerShape(16.dp), |
|
|
shape = RoundedCornerShape(16.dp), |
|
|
@ -233,10 +277,7 @@ fun PostCard( |
|
|
.fillMaxWidth() |
|
|
.fillMaxWidth() |
|
|
.padding(start = 10.dp, end = 10.dp) |
|
|
.padding(start = 10.dp, end = 10.dp) |
|
|
.height(360.dp), |
|
|
.height(360.dp), |
|
|
colors = CardDefaults.cardColors( |
|
|
colors = CardDefaults.cardColors(containerColor = Color.White) |
|
|
containerColor = Color.White // your background color |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
) { |
|
|
) { |
|
|
Box( |
|
|
Box( |
|
|
modifier = Modifier.fillMaxSize(), |
|
|
modifier = Modifier.fillMaxSize(), |
|
|
@ -256,21 +297,23 @@ fun PostCard( |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- 4. Interaction Buttons --- |
|
|
// --- 3. Interaction Buttons --- |
|
|
Row( |
|
|
Row( |
|
|
modifier = Modifier |
|
|
modifier = Modifier |
|
|
.fillMaxWidth() |
|
|
.fillMaxWidth() |
|
|
.padding(horizontal = 8.dp, vertical = 4.dp), |
|
|
.padding(horizontal = 8.dp, vertical = 4.dp), |
|
|
verticalAlignment = Alignment.CenterVertically |
|
|
verticalAlignment = Alignment.CenterVertically |
|
|
) { |
|
|
) { |
|
|
IconButton(onClick = { /* Handle like action */ }) { |
|
|
IconButton(onClick = { |
|
|
|
|
|
showPostViewModel.toggleLikeLocallyAndServer(context, post.id) |
|
|
|
|
|
}) { |
|
|
Icon( |
|
|
Icon( |
|
|
imageVector = if (post.liked_by_me) Icons.Filled.Favorite else Icons.Default.FavoriteBorder, |
|
|
imageVector = if (likedByMe) Icons.Filled.Favorite else Icons.Default.FavoriteBorder, |
|
|
contentDescription = "Like", |
|
|
contentDescription = "Like", |
|
|
tint = if (post.liked_by_me) Color.Red else Color.Black |
|
|
tint = if (likedByMe) Color.Red else Color.Black |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
Text(text = "${post.like_count}") |
|
|
Text(text = "$likeCount") |
|
|
|
|
|
|
|
|
Spacer(modifier = Modifier.width(16.dp)) |
|
|
Spacer(modifier = Modifier.width(16.dp)) |
|
|
|
|
|
|
|
|
@ -280,19 +323,71 @@ fun PostCard( |
|
|
contentDescription = "Comments" |
|
|
contentDescription = "Comments" |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
Text(text = "${comments.size}") |
|
|
Text(text = "$commentCount") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --- 4. Likers Summary Text --- |
|
|
|
|
|
val currentUserName = tokenManager.currentUserName() ?: "You" |
|
|
|
|
|
|
|
|
|
|
|
val displayLikersText = when { |
|
|
|
|
|
postLikers.isNotEmpty() -> { |
|
|
|
|
|
val primaryLikerName = postLikers.first().fullName |
|
|
|
|
|
val otherCount = postLikers.size - 1 |
|
|
|
|
|
if (otherCount > 0) { |
|
|
|
|
|
"$primaryLikerName and $otherCount other liked this" |
|
|
|
|
|
} else { |
|
|
|
|
|
"$primaryLikerName liked this" |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
likedByMe && likeCount > 0 -> { |
|
|
|
|
|
if (likeCount > 1) { |
|
|
|
|
|
"$currentUserName and ${likeCount - 1} other liked this" |
|
|
|
|
|
} else { |
|
|
|
|
|
"$currentUserName liked this" |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else -> null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (displayLikersText != null) { |
|
|
|
|
|
Text( |
|
|
|
|
|
text = displayLikersText, |
|
|
|
|
|
style = MaterialTheme.typography.bodySmall, |
|
|
|
|
|
fontWeight = FontWeight.Bold, |
|
|
|
|
|
color = Color.Gray, |
|
|
|
|
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 2.dp) |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// --- 5. Comment Section UI --- |
|
|
// --- 5. Comment Section UI --- |
|
|
CommentSection( |
|
|
CommentSection( |
|
|
comments = comments, |
|
|
comments = comments, |
|
|
postId = post.id, |
|
|
postId = post.id, |
|
|
postUserName = post.author?.full_name // see note below |
|
|
postUserName = post.author?.full_name, |
|
|
|
|
|
postUserId = post.author?.id |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- Delete confirmation dialog --- |
|
|
|
|
|
if (showDeleteDialog) { |
|
|
|
|
|
AlertDialog( |
|
|
|
|
|
onDismissRequest = { showDeleteDialog = false }, |
|
|
|
|
|
title = { Text("Delete post?") }, |
|
|
|
|
|
text = { Text("This can't be undone.") }, |
|
|
|
|
|
confirmButton = { |
|
|
|
|
|
TextButton(onClick = { |
|
|
|
|
|
showDeleteDialog = false |
|
|
|
|
|
showPostViewModel.deletePost(context, post.id) { |
|
|
|
|
|
(context as? Activity)?.finish() // close screen after delete |
|
|
|
|
|
} |
|
|
|
|
|
}) { Text("Delete", color = Color.Red) } |
|
|
|
|
|
}, |
|
|
|
|
|
dismissButton = { |
|
|
|
|
|
TextButton(onClick = { showDeleteDialog = false }) { Text("Cancel") } |
|
|
|
|
|
} |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@Composable |
|
|
@Composable |
|
|
fun CommentSection( |
|
|
fun CommentSection( |
|
|
@ -311,8 +406,6 @@ fun CommentSection( |
|
|
|
|
|
|
|
|
val tokenManager = remember { TokenManager.getInstance(context) } |
|
|
val tokenManager = remember { TokenManager.getInstance(context) } |
|
|
val currentUserName = remember { tokenManager.currentUserName() } |
|
|
val currentUserName = remember { tokenManager.currentUserName() } |
|
|
val currentProfileImage = remember { tokenManager.currentProfileImage() } |
|
|
|
|
|
// ✅ get the logged-in user's id once |
|
|
|
|
|
val currentUserId = remember { tokenManager.getUserId() } |
|
|
val currentUserId = remember { tokenManager.getUserId() } |
|
|
|
|
|
|
|
|
Column( |
|
|
Column( |
|
|
@ -380,8 +473,6 @@ fun CommentSection( |
|
|
comments.reversed().forEach { comment -> |
|
|
comments.reversed().forEach { comment -> |
|
|
val isEditing = editingCommentId == comment.id |
|
|
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 commentAuthorId = comment.author?.id |
|
|
val isCommentOwner = commentAuthorId != null && commentAuthorId == currentUserId |
|
|
val isCommentOwner = commentAuthorId != null && commentAuthorId == currentUserId |
|
|
val isPostOwner = postUserId != null && postUserId == currentUserId |
|
|
val isPostOwner = postUserId != null && postUserId == currentUserId |
|
|
@ -389,11 +480,6 @@ fun CommentSection( |
|
|
val canDelete = isCommentOwner || isPostOwner |
|
|
val canDelete = isCommentOwner || isPostOwner |
|
|
val hasAnyAction = canEdit || canDelete |
|
|
val hasAnyAction = canEdit || canDelete |
|
|
|
|
|
|
|
|
Log.d( |
|
|
|
|
|
"CommentPerm", |
|
|
|
|
|
"current=$currentUserId | commentAuthor=$commentAuthorId | postOwner=$postUserId | canEdit=$canEdit canDelete=$canDelete" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
Row( |
|
|
Row( |
|
|
modifier = Modifier |
|
|
modifier = Modifier |
|
|
.fillMaxWidth() |
|
|
.fillMaxWidth() |
|
|
@ -401,9 +487,9 @@ fun CommentSection( |
|
|
verticalAlignment = Alignment.CenterVertically |
|
|
verticalAlignment = Alignment.CenterVertically |
|
|
) { |
|
|
) { |
|
|
AsyncImage( |
|
|
AsyncImage( |
|
|
model = currentProfileImage, |
|
|
model = comment.author?.profile_image_url, |
|
|
contentDescription = "Post author image", |
|
|
contentDescription = "Comment author image", |
|
|
placeholder = painterResource(id = R.drawable.locationpermissionbg), // your existing placeholder |
|
|
placeholder = painterResource(id = R.drawable.locationpermissionbg), |
|
|
error = painterResource(id = R.drawable.locationpermissionbg), |
|
|
error = painterResource(id = R.drawable.locationpermissionbg), |
|
|
modifier = Modifier |
|
|
modifier = Modifier |
|
|
.size(32.dp) |
|
|
.size(32.dp) |
|
|
@ -419,7 +505,6 @@ fun CommentSection( |
|
|
.fillMaxWidth() |
|
|
.fillMaxWidth() |
|
|
.clip(RoundedCornerShape(12.dp)) |
|
|
.clip(RoundedCornerShape(12.dp)) |
|
|
.background(CommentBubbleBg) |
|
|
.background(CommentBubbleBg) |
|
|
// ✅ only open the menu if the user can actually do something |
|
|
|
|
|
.clickable(enabled = !isEditing && hasAnyAction) { |
|
|
.clickable(enabled = !isEditing && hasAnyAction) { |
|
|
menuCommentId = comment.id |
|
|
menuCommentId = comment.id |
|
|
} |
|
|
} |
|
|
@ -450,12 +535,7 @@ fun CommentSection( |
|
|
|
|
|
|
|
|
TextButton(onClick = { |
|
|
TextButton(onClick = { |
|
|
if (editText.isNotBlank()) { |
|
|
if (editText.isNotBlank()) { |
|
|
// viewModel.editComment( |
|
|
// viewModel.editComment(...) not implemented yet |
|
|
// context = context, |
|
|
|
|
|
// postId = postId, |
|
|
|
|
|
// commentId = comment.id, |
|
|
|
|
|
// newContent = editText |
|
|
|
|
|
// ) |
|
|
|
|
|
editingCommentId = null |
|
|
editingCommentId = null |
|
|
editText = "" |
|
|
editText = "" |
|
|
} |
|
|
} |
|
|
@ -473,7 +553,6 @@ fun CommentSection( |
|
|
expanded = menuCommentId == comment.id, |
|
|
expanded = menuCommentId == comment.id, |
|
|
onDismissRequest = { menuCommentId = null } |
|
|
onDismissRequest = { menuCommentId = null } |
|
|
) { |
|
|
) { |
|
|
// ✅ Edit: only the comment author |
|
|
|
|
|
if (canEdit) { |
|
|
if (canEdit) { |
|
|
DropdownMenuItem( |
|
|
DropdownMenuItem( |
|
|
text = { Text("Edit") }, |
|
|
text = { Text("Edit") }, |
|
|
@ -484,7 +563,6 @@ fun CommentSection( |
|
|
} |
|
|
} |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
// ✅ Delete: comment author OR post owner |
|
|
|
|
|
if (canDelete) { |
|
|
if (canDelete) { |
|
|
DropdownMenuItem( |
|
|
DropdownMenuItem( |
|
|
text = { Text("Delete") }, |
|
|
text = { Text("Delete") }, |
|
|
@ -506,11 +584,14 @@ fun CommentSection( |
|
|
Spacer(modifier = Modifier.height(8.dp)) |
|
|
Spacer(modifier = Modifier.height(8.dp)) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) |
|
|
@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) |
|
|
@Composable |
|
|
@Composable |
|
|
fun ShowPostScreenPreview() { |
|
|
fun ShowPostScreenPreview() { |
|
|
DattingAPPTheme { |
|
|
DattingAPPTheme { |
|
|
// Mock sample data mirroring your API schema |
|
|
val context = LocalContext.current |
|
|
|
|
|
val showPostViewModel: ShowPostViewModel = viewModel() |
|
|
|
|
|
|
|
|
val mockPost = ShowPostResponse( |
|
|
val mockPost = ShowPostResponse( |
|
|
id = 21, |
|
|
id = 21, |
|
|
content = "Exploring the city and enjoying some coffee! ☕🏙️ #Android #JetpackCompose", |
|
|
content = "Exploring the city and enjoying some coffee! ☕🏙️ #Android #JetpackCompose", |
|
|
@ -527,7 +608,7 @@ fun ShowPostScreenPreview() { |
|
|
full_name = "Alex Rivera", |
|
|
full_name = "Alex Rivera", |
|
|
city = "Shanghai", |
|
|
city = "Shanghai", |
|
|
country = "China", |
|
|
country = "China", |
|
|
profile_image_url=null, |
|
|
profile_image_url = null, |
|
|
compatibilityPercentage = 95 |
|
|
compatibilityPercentage = 95 |
|
|
), |
|
|
), |
|
|
like_count = 12, |
|
|
like_count = 12, |
|
|
@ -546,7 +627,6 @@ fun ShowPostScreenPreview() { |
|
|
createdAt = "2026-10-13T06:06:15" |
|
|
createdAt = "2026-10-13T06:06:15" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
// Mock comments list |
|
|
|
|
|
val mockComments = listOf( |
|
|
val mockComments = listOf( |
|
|
CommentResponse( |
|
|
CommentResponse( |
|
|
id = 1, |
|
|
id = 1, |
|
|
@ -557,7 +637,7 @@ fun ShowPostScreenPreview() { |
|
|
full_name = "Sarah", |
|
|
full_name = "Sarah", |
|
|
city = "Shanghai", |
|
|
city = "Shanghai", |
|
|
country = "China", |
|
|
country = "China", |
|
|
profile_image_url=null, |
|
|
profile_image_url = null, |
|
|
compatibilityPercentage = 100 |
|
|
compatibilityPercentage = 100 |
|
|
), |
|
|
), |
|
|
createdAt = "2026-10-13T07:45:15" |
|
|
createdAt = "2026-10-13T07:45:15" |
|
|
@ -571,14 +651,19 @@ fun ShowPostScreenPreview() { |
|
|
full_name = "David", |
|
|
full_name = "David", |
|
|
city = "Beijing", |
|
|
city = "Beijing", |
|
|
country = "China", |
|
|
country = "China", |
|
|
profile_image_url=null, |
|
|
profile_image_url = null, |
|
|
compatibilityPercentage = 90 |
|
|
compatibilityPercentage = 90 |
|
|
), |
|
|
), |
|
|
createdAt = "2026-10-13T08:04:48" |
|
|
createdAt = "2026-10-13T08:04:48" |
|
|
) |
|
|
) |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
// Full screen container matching activity layout |
|
|
val mockLikers = listOf( |
|
|
|
|
|
PostAllLikerResponse(id = 1, fullName = "Parvej"), |
|
|
|
|
|
PostAllLikerResponse(id = 2, fullName = "Sarah"), |
|
|
|
|
|
PostAllLikerResponse(id = 3, fullName = "David") |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
Column( |
|
|
Column( |
|
|
modifier = Modifier |
|
|
modifier = Modifier |
|
|
.fillMaxSize() |
|
|
.fillMaxSize() |
|
|
@ -586,8 +671,11 @@ fun ShowPostScreenPreview() { |
|
|
.padding(vertical = 16.dp) |
|
|
.padding(vertical = 16.dp) |
|
|
) { |
|
|
) { |
|
|
PostCard( |
|
|
PostCard( |
|
|
|
|
|
context = context, |
|
|
|
|
|
showPostViewModel = showPostViewModel, |
|
|
post = mockPost, |
|
|
post = mockPost, |
|
|
comments = mockComments, |
|
|
comments = mockComments, |
|
|
|
|
|
postLikers = mockLikers |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|