dm-padding message-9

This commit is contained in:
eric 2026-07-08 14:30:25 +08:00
parent 4d5d5ca916
commit 47fb10d4e4
7 changed files with 70 additions and 8 deletions

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AppInsightsSettings">
<option name="tabSettings">
<map>
<entry key="Firebase Crashlytics">
<value>
<InsightsFilterSettings>
<option name="connection">
<ConnectionSetting>
<option name="appId" value="PLACEHOLDER" />
<option name="mobileSdkAppId" value="" />
<option name="projectId" value="" />
<option name="projectNumber" value="" />
</ConnectionSetting>
</option>
<option name="signal" value="SIGNAL_UNSPECIFIED" />
<option name="timeIntervalDays" value="THIRTY_DAYS" />
<option name="visibilityType" value="ALL" />
</InsightsFilterSettings>
</value>
</entry>
</map>
</option>
</component>
</project>

6
.idea/studiobot.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="StudioBotProjectSettings">
<option name="shareContext" value="OptedIn" />
</component>
</project>

View file

@ -108,8 +108,8 @@ interface TecnologiaApi {
@GET("api/dm/{room_id}/messages") @GET("api/dm/{room_id}/messages")
suspend fun getChatMessages( suspend fun getChatMessages(
@Path("room_id") roomId: String, @Path("room_id") roomId: String,
@Query("cursor") cursor: String? = null, // 🎯 新增這個:載入歷史紀錄時帶入上一次的 nextCursor @Query("before_id") cursor: String? = null, // 🎯 修正為與後端文件一致的 before_id
@Query("limit") limit: Int? = null // 💡 選填:如果後端有支援控制數量的話可以加上 @Query("limit") limit: Int? = null
): Response<MessageResponse> ): Response<MessageResponse>
// 標記聊天室已讀 // 標記聊天室已讀

View file

@ -73,9 +73,18 @@ object NetworkModule {
tokenInterceptor: Interceptor, tokenInterceptor: Interceptor,
tokenAuthenticator: TokenAuthenticator // 👈 注入前面實作好的 Authenticator tokenAuthenticator: TokenAuthenticator // 👈 注入前面實作好的 Authenticator
): OkHttpClient { ): OkHttpClient {
val loggingInterceptor = Interceptor { chain ->
val request = chain.request()
android.util.Log.d("ChatDebug", "🚀 [Network] Request: ${request.method} ${request.url}")
val response = chain.proceed(request)
android.util.Log.d("ChatDebug", "📦 [Network] Response: ${response.code} for ${request.url}")
response
}
return OkHttpClient.Builder() return OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor) // 負責 Log 網路請求
.addInterceptor(tokenInterceptor) // 負責幫一般請求加上 Token .addInterceptor(tokenInterceptor) // 負責幫一般請求加上 Token
.authenticator(tokenAuthenticator) // 負責在 401 時自動刷新 Token .authenticator(tokenAuthenticator) // 負責在 401 時自動刷新 Token
.build() .build()

View file

@ -180,13 +180,17 @@ class ChatRoomsRepository @Inject constructor(
cursor: String? = null, cursor: String? = null,
limit: Int? = null limit: Int? = null
): Result<MessageResponse> { ): Result<MessageResponse> {
Log.d("ChatDebug", "📡 [Repository] refreshChatMessages 觸發 | roomId: $roomId | cursor: $cursor | limit: $limit")
return try { return try {
// 1. 把 cursor 傳給 API // 1. 把 cursor 與 limit 傳給 API
val response = api.getChatMessages(roomId, cursor = cursor) val response = api.getChatMessages(roomId, cursor = cursor, limit = limit)
if (response.isSuccessful) { if (response.isSuccessful) {
val body = response.body() val body = response.body()
if (body != null) { if (body != null) {
val firstId = body.messages.firstOrNull()?.id
val lastId = body.messages.lastOrNull()?.id
Log.d("ChatDebug", "✅ [Repository] API 成功 | 收到 ${body.messages.size} 條訊息 | 首條ID: $firstId | 末條ID: $lastId | nextCursor: ${body.nextCursor}")
// 2. 轉換成 Entity 列表 // 2. 轉換成 Entity 列表
val entities = body.messages.map { networkMessage -> val entities = body.messages.map { networkMessage ->

View file

@ -165,6 +165,12 @@ fun MessageMessaging(
) { uri: Uri? -> ) { uri: Uri? ->
uri?.let { viewModel.uploadAttachment(roomId, it) } uri?.let { viewModel.uploadAttachment(roomId, it) }
} }
//進入時重整
LaunchedEffect(roomId) {
viewModel.loadMessages(roomId)
}
//bottom bar on and off //bottom bar on and off
DisposableEffect(Unit) { DisposableEffect(Unit) {
bottomBarViewModel.setVisible(false) bottomBarViewModel.setVisible(false)

View file

@ -190,10 +190,21 @@ class ChatViewModel @Inject constructor(
// 💡 核心新增:往上滑載入更多老訊息 (補上 limit) // 💡 核心新增:往上滑載入更多老訊息 (補上 limit)
// ===================================================================== // =====================================================================
fun loadMoreMessages(roomId: String) { fun loadMoreMessages(roomId: String) {
if (isLoadingMore || !hasMore || nextCursor.isNullOrBlank()) return if (isLoadingMore) {
Log.d("ChatDebug", "⚠️ [ViewModel] loadMoreMessages 略過: 正在加載中...")
return
}
if (!hasMore) {
Log.d("ChatDebug", "⚠️ [ViewModel] loadMoreMessages 略過: 沒有更多訊息了 (hasMore=false)")
return
}
if (nextCursor.isNullOrBlank()) {
Log.d("ChatDebug", "⚠️ [ViewModel] loadMoreMessages 略過: nextCursor 為空")
return
}
isLoadingMore = true isLoadingMore = true
Log.d("ChatViewModel", "觸發載入更多,當前 cursor: $nextCursor") Log.d("ChatDebug", "🚀 [ViewModel] 開始載入更多 | 當前 cursor: $nextCursor | roomId: $roomId")
viewModelScope.launch { viewModelScope.launch {
// 🎯 補上 limit = 20 傳遞給 Repository 與 API // 🎯 補上 limit = 20 傳遞給 Repository 與 API
@ -201,10 +212,10 @@ class ChatViewModel @Inject constructor(
.onSuccess { response -> .onSuccess { response ->
this@ChatViewModel.nextCursor = response.nextCursor this@ChatViewModel.nextCursor = response.nextCursor
this@ChatViewModel.hasMore = response.hasMore this@ChatViewModel.hasMore = response.hasMore
Log.d("ChatViewModel", "載入更多成功,新 cursor: $nextCursor, hasMore: $hasMore") Log.d("ChatDebug", "✅ [ViewModel] 載入更多成功 | 新 cursor: $nextCursor | hasMore: $hasMore")
} }
.onFailure { error -> .onFailure { error ->
Log.e("ChatViewModel", "載入更多老訊息失敗", error) Log.e("ChatDebug", "❌ [ViewModel] 載入更多失敗", error)
} }
isLoadingMore = false isLoadingMore = false