dm-138
This commit is contained in:
parent
f753659a6e
commit
37d7eb6528
2 changed files with 95 additions and 3 deletions
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.example.login_v3.home.Message.UI.Detail.Message_Component
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ImageLightbox(
|
||||||
|
imageUrl: String,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
// 使用 Dialog 才能真正做到全螢幕蓋住 TopBar 與 BottomBar
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
properties = DialogProperties(
|
||||||
|
usePlatformDefaultWidth = false // 關閉預設寬度限制,達到真正全螢幕
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Black.copy(alpha = 0.9f)) // 質感黑半透明背景
|
||||||
|
.clickable { onDismiss() }, // 點擊背景任何地方都能關閉
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
model = imageUrl,
|
||||||
|
contentDescription = "放大檢視圖片",
|
||||||
|
contentScale = ContentScale.Fit, // 確保整張圖片能完整塞進螢幕
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight(0.85f) // 留一點上下操作空間
|
||||||
|
.clickable(enabled = false) { } // 阻止點擊圖片本身時觸發關閉
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -77,6 +77,7 @@ import androidx.compose.material.icons.filled.Call
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import com.example.login_v3.data.api.api_class.CallLogInfo
|
import com.example.login_v3.data.api.api_class.CallLogInfo
|
||||||
import com.example.login_v3.home.Message.UI.Detail.Message_Component.CallLogBubble
|
import com.example.login_v3.home.Message.UI.Detail.Message_Component.CallLogBubble
|
||||||
|
import com.example.login_v3.home.Message.UI.Detail.Message_Component.ImageLightbox
|
||||||
import com.example.login_v3.home.Message.UI.Detail.Message_Component.UserStatusDot
|
import com.example.login_v3.home.Message.UI.Detail.Message_Component.UserStatusDot
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -112,6 +113,9 @@ fun MessageMessaging(
|
||||||
|
|
||||||
var emojiTargetMessage by remember { mutableStateOf<Message?>(null) }
|
var emojiTargetMessage by remember { mutableStateOf<Message?>(null) }
|
||||||
|
|
||||||
|
//inspect image
|
||||||
|
var lightboxImageUrl by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
// 🌟 核心:使用 derivedStateOf 統一控管狀態權限(長按優先權通常大於單擊)
|
// 🌟 核心:使用 derivedStateOf 統一控管狀態權限(長按優先權通常大於單擊)
|
||||||
val bottomBarState by remember {
|
val bottomBarState by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
|
@ -236,7 +240,7 @@ fun MessageMessaging(
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = Color(0xFF333333),
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
|
|
@ -425,8 +429,41 @@ fun MessageMessaging(
|
||||||
},
|
},
|
||||||
// 💡 單擊事件:開啟 Emoji 選單,同時清空長按選單
|
// 💡 單擊事件:開啟 Emoji 選單,同時清空長按選單
|
||||||
onRowClick = { message ->
|
onRowClick = { message ->
|
||||||
viewModel.clearActionMessage()
|
val isAttachmentImage = message.attachment?.mimeType?.startsWith("image/") == true
|
||||||
emojiTargetMessage = message
|
val contentLowerCase = (message.content ?: "").lowercase()
|
||||||
|
val imageExtensions = listOf(".jpg", ".jpeg", ".png", ".webp", ".heic", ".heif")
|
||||||
|
val isLegacyImage = imageExtensions.any { contentLowerCase.contains(it) }
|
||||||
|
|
||||||
|
if (isAttachmentImage || isLegacyImage) {
|
||||||
|
// 🎯 A 狀態:這是圖片,計算圖片完整 URL 並塞入 lightbox 狀態觸發放大
|
||||||
|
val rawPath = if (isAttachmentImage && !message.attachment?.filename.isNullOrBlank()) {
|
||||||
|
message.attachment!!.filename
|
||||||
|
} else {
|
||||||
|
message.content ?: ""
|
||||||
|
}
|
||||||
|
val baseUrl = "https://tg.technologia-tw.com"
|
||||||
|
val finalUrl = when {
|
||||||
|
rawPath.isBlank() -> ""
|
||||||
|
rawPath.startsWith("http") -> rawPath
|
||||||
|
else -> {
|
||||||
|
val cleanPath = rawPath.removePrefix("/").removePrefix("uploads/")
|
||||||
|
if (cleanPath.startsWith("attachment/")) {
|
||||||
|
"$baseUrl/uploads/$cleanPath"
|
||||||
|
} else {
|
||||||
|
"$baseUrl/uploads/attachment/$cleanPath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 關閉可能開啟的選單,並打開大圖
|
||||||
|
viewModel.clearActionMessage()
|
||||||
|
emojiTargetMessage = null
|
||||||
|
lightboxImageUrl = finalUrl
|
||||||
|
} else {
|
||||||
|
// 🎯 B 狀態:一般文字或通話,維持原本點擊叫出 Emoji Bar 的邏輯
|
||||||
|
viewModel.clearActionMessage()
|
||||||
|
emojiTargetMessage = message
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onReactionClick = { message, emoji ->
|
onReactionClick = { message, emoji ->
|
||||||
// 這是訊息氣泡下方既有 Reaction 小標籤的點擊事件,保持原樣即可
|
// 這是訊息氣泡下方既有 Reaction 小標籤的點擊事件,保持原樣即可
|
||||||
|
|
@ -452,6 +489,13 @@ fun MessageMessaging(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lightboxImageUrl?.let { url ->
|
||||||
|
ImageLightbox(
|
||||||
|
imageUrl = url,
|
||||||
|
onDismiss = { lightboxImageUrl = null }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue