auth to server-V77
This commit is contained in:
parent
a35d4412f3
commit
f7a3f64d61
5 changed files with 103 additions and 9 deletions
|
|
@ -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<Unit>
|
||||
|
||||
// 搜尋使用者
|
||||
@GET("api/users/search")
|
||||
suspend fun searchUsers(
|
||||
@Query("q") query: String
|
||||
): UserSearchResponse
|
||||
|
||||
}
|
||||
|
|
@ -79,3 +79,13 @@ val PendingFriendApiModel.fullPendingAvatarUrl: Any
|
|||
} else {
|
||||
"http://192.168.0.217$pendingAvatarUrl"
|
||||
}
|
||||
|
||||
// 根據你說的 API 回傳格式定義
|
||||
data class UserSearchResponse(
|
||||
val additionalProp1: Map<String, UserDetail> // 假設它是一個物件對應
|
||||
)
|
||||
|
||||
data class UserDetail(
|
||||
val id: String,
|
||||
val name: String
|
||||
)
|
||||
|
|
@ -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<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> {
|
||||
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<Unit> {
|
||||
return safeApiCall {
|
||||
// 確保這裡的參數名稱與 Data Class 定義的一模一樣
|
||||
val request = AddFriendRequest(friendId = friendId)
|
||||
apiService.sendFriendRequest(request)
|
||||
// 搜尋使用者
|
||||
suspend fun searchUsers(keyword: String): Result<List<UserDetail>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<Friend> = emptyList(),
|
||||
val pendingRequests: List<PendingFriendApiModel> = emptyList(),
|
||||
// 新增:搜尋結果與關鍵字
|
||||
val searchResults: List<UserDetail> = 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}"
|
||||
)}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue