auth to server-V77

This commit is contained in:
Eric85710 2026-05-10 15:27:11 +08:00
parent a35d4412f3
commit f7a3f64d61
5 changed files with 103 additions and 9 deletions

View file

@ -11,6 +11,7 @@ import retrofit2.http.PATCH
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Part import retrofit2.http.Part
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query
interface TecnologiaApi { interface TecnologiaApi {
@ -72,4 +73,10 @@ interface TecnologiaApi {
@Body request: AddFriendRequest @Body request: AddFriendRequest
): Response<Unit> ): Response<Unit>
// 搜尋使用者
@GET("api/users/search")
suspend fun searchUsers(
@Query("q") query: String
): UserSearchResponse
} }

View file

@ -79,3 +79,13 @@ val PendingFriendApiModel.fullPendingAvatarUrl: Any
} else { } else {
"http://192.168.0.217$pendingAvatarUrl" "http://192.168.0.217$pendingAvatarUrl"
} }
// 根據你說的 API 回傳格式定義
data class UserSearchResponse(
val additionalProp1: Map<String, UserDetail> // 假設它是一個物件對應
)
data class UserDetail(
val id: String,
val name: String
)

View file

@ -1,9 +1,11 @@
package com.example.login_v3.data.repository.basic 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.TecnologiaApi
import com.example.login_v3.data.api.api_class.AddFriendRequest 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.Friend
import com.example.login_v3.data.api.api_class.PendingFriendApiModel import com.example.login_v3.data.api.api_class.PendingFriendApiModel
import com.example.login_v3.data.api.api_class.UserDetail
import retrofit2.Response import retrofit2.Response
import javax.inject.Inject import javax.inject.Inject
@ -45,23 +47,51 @@ class FriendsRepository @Inject constructor(
apiService.rejectFriendRequest(id) apiService.rejectFriendRequest(id)
} }
// 統一處理 Response<Unit> 的工具函式 //add friends
suspend fun sendFriendRequest(friendId: String): Result<Unit> {
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<Unit>): Result<Unit> { private suspend fun safeApiCall(call: suspend () -> Response<Unit>): Result<Unit> {
return try { return try {
val response = call() val response = call()
if (response.isSuccessful) Result.success(Unit) if (response.isSuccessful) {
else Result.failure(Exception("API 錯誤: ${response.code()}")) 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) { } catch (e: Exception) {
Log.e("API_DEBUG", "連線或執行異常: ${e.message}", e)
Result.failure(e) Result.failure(e)
} }
} }
//add friends // 搜尋使用者
suspend fun sendFriendRequest(friendId: String): Result<Unit> { suspend fun searchUsers(keyword: String): Result<List<UserDetail>> {
return safeApiCall { return try {
// 確保這裡的參數名稱與 Data Class 定義的一模一樣 val response = apiService.searchUsers(keyword)
val request = AddFriendRequest(friendId = friendId)
apiService.sendFriendRequest(request) // 這裡做一個「結構轉換」
// 假設 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)
} }
} }

View file

@ -61,6 +61,8 @@ fun Message_contact_list(
) { ) {
// 統一觀察 uiState 即可,避免觀察多個 Flow 導致狀態不同步 // 統一觀察 uiState 即可,避免觀察多個 Flow 導致狀態不同步
val uiState by contactListViewModel.uiState.collectAsStateWithLifecycle() val uiState by contactListViewModel.uiState.collectAsStateWithLifecycle()
// 判斷目前是否處於「搜尋模式」
val isSearching = uiState.searchQuery.isNotBlank()
val friends = uiState.friends // 直接從 uiState 裡面取 val friends = uiState.friends // 直接從 uiState 裡面取
DisposableEffect(Unit) { DisposableEffect(Unit) {

View file

@ -5,8 +5,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.login_v3.data.api.api_class.Friend 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.PendingFriendApiModel
import com.example.login_v3.data.api.api_class.UserDetail
import com.example.login_v3.data.repository.basic.FriendsRepository import com.example.login_v3.data.repository.basic.FriendsRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@ -19,6 +22,9 @@ data class ContactUiState(
val isLoading: Boolean = false, val isLoading: Boolean = false,
val friends: List<Friend> = emptyList(), val friends: List<Friend> = emptyList(),
val pendingRequests: List<PendingFriendApiModel> = emptyList(), val pendingRequests: List<PendingFriendApiModel> = emptyList(),
// 新增:搜尋結果與關鍵字
val searchResults: List<UserDetail> = emptyList(),
val searchQuery: String = "",
val error: String? = null val error: String? = null
) )
@HiltViewModel @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}"
)}
}
}
} }