From f7a3f64d610bd357c26a6be0350088b48f3dc425 Mon Sep 17 00:00:00 2001 From: Eric85710 Date: Sun, 10 May 2026 15:27:11 +0800 Subject: [PATCH] auth to server-V77 --- .../login_v3/data/api/Main_API_Service.kt | 7 +++ .../data/api/api_class/FriendRequest.kt | 10 ++++ .../repository/basic/Friends_Repository.kt | 48 +++++++++++++++---- .../Message/UI/Detail/Message_contact_list.kt | 2 + .../Detail/Contact_list_ViewModel.kt | 45 +++++++++++++++++ 5 files changed, 103 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/example/login_v3/data/api/Main_API_Service.kt b/app/src/main/java/com/example/login_v3/data/api/Main_API_Service.kt index 185747f..053d9f4 100644 --- a/app/src/main/java/com/example/login_v3/data/api/Main_API_Service.kt +++ b/app/src/main/java/com/example/login_v3/data/api/Main_API_Service.kt @@ -11,6 +11,7 @@ import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Part import retrofit2.http.Path +import retrofit2.http.Query interface TecnologiaApi { @@ -72,4 +73,10 @@ interface TecnologiaApi { @Body request: AddFriendRequest ): Response + // 搜尋使用者 + @GET("api/users/search") + suspend fun searchUsers( + @Query("q") query: String + ): UserSearchResponse + } \ No newline at end of file diff --git a/app/src/main/java/com/example/login_v3/data/api/api_class/FriendRequest.kt b/app/src/main/java/com/example/login_v3/data/api/api_class/FriendRequest.kt index 2020761..e56fa16 100644 --- a/app/src/main/java/com/example/login_v3/data/api/api_class/FriendRequest.kt +++ b/app/src/main/java/com/example/login_v3/data/api/api_class/FriendRequest.kt @@ -79,3 +79,13 @@ val PendingFriendApiModel.fullPendingAvatarUrl: Any } else { "http://192.168.0.217$pendingAvatarUrl" } + +// 根據你說的 API 回傳格式定義 +data class UserSearchResponse( + val additionalProp1: Map // 假設它是一個物件對應 +) + +data class UserDetail( + val id: String, + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/login_v3/data/repository/basic/Friends_Repository.kt b/app/src/main/java/com/example/login_v3/data/repository/basic/Friends_Repository.kt index fbbf9bd..2bf5321 100644 --- a/app/src/main/java/com/example/login_v3/data/repository/basic/Friends_Repository.kt +++ b/app/src/main/java/com/example/login_v3/data/repository/basic/Friends_Repository.kt @@ -1,9 +1,11 @@ package com.example.login_v3.data.repository.basic +import android.util.Log import com.example.login_v3.data.api.TecnologiaApi import com.example.login_v3.data.api.api_class.AddFriendRequest import com.example.login_v3.data.api.api_class.Friend import com.example.login_v3.data.api.api_class.PendingFriendApiModel +import com.example.login_v3.data.api.api_class.UserDetail import retrofit2.Response import javax.inject.Inject @@ -45,23 +47,51 @@ class FriendsRepository @Inject constructor( apiService.rejectFriendRequest(id) } - // 統一處理 Response 的工具函式 + //add friends + suspend fun sendFriendRequest(friendId: String): Result { + Log.d("API_DEBUG", "準備發送好友申請,目標 ID: $friendId") + val result = safeApiCall { + val request = AddFriendRequest(friendId = friendId) + apiService.sendFriendRequest(request) + } + result.onFailure { Log.e("API_DEBUG", "sendFriendRequest 最終失敗: ${it.message}") } + return result + } + + // 2. 確保「全類別」只有這一個 safeApiCall 函式 private suspend fun safeApiCall(call: suspend () -> Response): Result { return try { val response = call() - if (response.isSuccessful) Result.success(Unit) - else Result.failure(Exception("API 錯誤: ${response.code()}")) + if (response.isSuccessful) { + Log.d("API_DEBUG", "請求成功: ${response.raw().request.url}") + Result.success(Unit) + } else { + val errorBody = response.errorBody()?.string() + val errorCode = response.code() + val errorMessage = "API 錯誤 $errorCode: $errorBody" + Log.e("API_DEBUG", errorMessage) + Result.failure(Exception(errorMessage)) + } } catch (e: Exception) { + Log.e("API_DEBUG", "連線或執行異常: ${e.message}", e) Result.failure(e) } } - //add friends - suspend fun sendFriendRequest(friendId: String): Result { - return safeApiCall { - // 確保這裡的參數名稱與 Data Class 定義的一模一樣 - val request = AddFriendRequest(friendId = friendId) - apiService.sendFriendRequest(request) + // 搜尋使用者 + suspend fun searchUsers(keyword: String): Result> { + return try { + val response = apiService.searchUsers(keyword) + + // 這裡做一個「結構轉換」 + // 假設 additionalProp1 裡面裝的是我們想要的資料 + // 如果 response.additionalProp1 是 Map,我們可以取其 Values 轉成 List + val userList = response.additionalProp1.values.toList() + + Result.success(userList) + } catch (e: Exception) { + Log.e("API_DEBUG", "搜尋失敗: ${e.message}") + Result.failure(e) } } diff --git a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_contact_list.kt b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_contact_list.kt index 56ba2f1..09be556 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_contact_list.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_contact_list.kt @@ -61,6 +61,8 @@ fun Message_contact_list( ) { // 統一觀察 uiState 即可,避免觀察多個 Flow 導致狀態不同步 val uiState by contactListViewModel.uiState.collectAsStateWithLifecycle() + // 判斷目前是否處於「搜尋模式」 + val isSearching = uiState.searchQuery.isNotBlank() val friends = uiState.friends // 直接從 uiState 裡面取 DisposableEffect(Unit) { diff --git a/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Contact_list_ViewModel.kt b/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Contact_list_ViewModel.kt index 36b2c80..b5ae73c 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Contact_list_ViewModel.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Contact_list_ViewModel.kt @@ -5,8 +5,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.login_v3.data.api.api_class.Friend import com.example.login_v3.data.api.api_class.PendingFriendApiModel +import com.example.login_v3.data.api.api_class.UserDetail import com.example.login_v3.data.repository.basic.FriendsRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -19,6 +22,9 @@ data class ContactUiState( val isLoading: Boolean = false, val friends: List = emptyList(), val pendingRequests: List = emptyList(), + // 新增:搜尋結果與關鍵字 + val searchResults: List = emptyList(), + val searchQuery: String = "", val error: String? = null ) @HiltViewModel @@ -112,4 +118,43 @@ class ContactListViewModel @Inject constructor( } } } + + private var searchJob: Job? = null + + fun onSearchQueryChanged(newQuery: String) { + // 更新 UI 上的輸入文字 + _uiState.update { it.copy(searchQuery = newQuery) } + + // 如果關鍵字為空,清空搜尋結果並返回 + if (newQuery.isBlank()) { + _uiState.update { it.copy(searchResults = emptyList()) } + return + } + + // --- 防抖處理 (Debounce) --- + searchJob?.cancel() // 取消前一個還在跑的搜尋 + searchJob = viewModelScope.launch { + delay(500) // 使用者停頓 0.5 秒後才真正發出請求 + performSearch(newQuery) + } + } + + private suspend fun performSearch(query: String) { + _uiState.update { it.copy(isLoading = true) } + + val result = repository.searchUsers(query) + + result.onSuccess { users -> + _uiState.update { it.copy( + isLoading = false, + searchResults = users, + error = null + )} + }.onFailure { error -> + _uiState.update { it.copy( + isLoading = false, + error = "搜尋失敗: ${error.message}" + )} + } + } } \ No newline at end of file