auth to server-V80
This commit is contained in:
parent
64fc6e7a6f
commit
2ec5028b3e
4 changed files with 75 additions and 48 deletions
|
|
@ -77,6 +77,6 @@ interface TecnologiaApi {
|
|||
@GET("api/users/search")
|
||||
suspend fun searchUsers(
|
||||
@Query("q") query: String
|
||||
): UserSearchResponse
|
||||
): Response<UserSearchResponse>
|
||||
|
||||
}
|
||||
|
|
@ -85,18 +85,29 @@ data class AddFriendRequest(
|
|||
)
|
||||
|
||||
//user search
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UserSearchResponse(
|
||||
val count: Int,
|
||||
val users: List<UserDetail>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true) // 內部類別也一定要加!
|
||||
data class UserDetail(
|
||||
val id: String,
|
||||
val username: String,
|
||||
val display_name: String,
|
||||
val avatar_url: String?, // 考慮到可能是 null,建議加上 ?
|
||||
val banner_url: String?,
|
||||
|
||||
@Json(name = "display_name") // 這樣你的 Kotlin 代碼可以用漂亮的 displayName
|
||||
val displayName: String,
|
||||
|
||||
@Json(name = "avatar_url")
|
||||
val avatarUrl: String?,
|
||||
|
||||
@Json(name = "banner_url")
|
||||
val bannerUrl: String?,
|
||||
|
||||
val bio: String?,
|
||||
val status: String?,
|
||||
val created_at: String
|
||||
|
||||
@Json(name = "created_at")
|
||||
val createdAt: String
|
||||
)
|
||||
|
|
@ -6,6 +6,7 @@ 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 com.example.login_v3.data.api.api_class.UserSearchResponse
|
||||
import retrofit2.Response
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -58,6 +59,34 @@ class FriendsRepository @Inject constructor(
|
|||
return result
|
||||
}
|
||||
|
||||
|
||||
//search user
|
||||
suspend fun searchUsers(query: String): Result<UserSearchResponse> {
|
||||
return safeApiCallWithData {
|
||||
apiService.searchUsers(query) // 1. 使用 apiService 2. 確保 apiService 定義回傳 Response<UserSearchResponse>
|
||||
}
|
||||
}
|
||||
|
||||
// 支援「回傳資料」的通用封裝 for search user
|
||||
private suspend fun <T> safeApiCallWithData(call: suspend () -> Response<T>): Result<T> {
|
||||
return try {
|
||||
val response = call()
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body()
|
||||
if (body != null) {
|
||||
Result.success(body)
|
||||
} else {
|
||||
Result.failure(Exception("回傳資料為空"))
|
||||
}
|
||||
} else {
|
||||
val errorMsg = "API 錯誤 ${response.code()}: ${response.errorBody()?.string()}"
|
||||
Result.failure(Exception(errorMsg))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 確保「全類別」只有這一個 safeApiCall 函式
|
||||
private suspend fun safeApiCall(call: suspend () -> Response<Unit>): Result<Unit> {
|
||||
return try {
|
||||
|
|
@ -78,21 +107,4 @@ class FriendsRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// 搜尋使用者
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -119,42 +119,46 @@ class ContactListViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var searchJob: Job? = null
|
||||
|
||||
|
||||
//search user
|
||||
fun onSearchQueryChanged(newQuery: String) {
|
||||
// 更新 UI 上的輸入文字
|
||||
// 1. 即時更新文字輸入,確保 UI TextField 顯示順暢
|
||||
_uiState.update { it.copy(searchQuery = newQuery) }
|
||||
}
|
||||
|
||||
// 如果關鍵字為空,清空搜尋結果並返回
|
||||
if (newQuery.isBlank()) {
|
||||
fun performSearch() {
|
||||
val query = _uiState.value.searchQuery
|
||||
if (query.isBlank()) {
|
||||
// 如果關鍵字為空,清空搜尋結果
|
||||
_uiState.update { it.copy(searchResults = emptyList()) }
|
||||
return
|
||||
}
|
||||
|
||||
// --- 防抖處理 (Debounce) ---
|
||||
searchJob?.cancel() // 取消前一個還在跑的搜尋
|
||||
searchJob = viewModelScope.launch {
|
||||
delay(500) // 使用者停頓 0.5 秒後才真正發出請求
|
||||
performSearch(newQuery)
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
// 2. 顯示 Loading
|
||||
_uiState.update { it.copy(isLoading = true, error = null) }
|
||||
|
||||
private suspend fun performSearch(query: String) {
|
||||
_uiState.update { it.copy(isLoading = true) }
|
||||
// 3. 呼叫 Repository
|
||||
val result = repository.searchUsers(query)
|
||||
|
||||
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}"
|
||||
)}
|
||||
// 4. 處理結果
|
||||
result.onSuccess { response ->
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
searchResults = response.users // UserSearchResponse 裡的列表
|
||||
)
|
||||
}
|
||||
}.onFailure { error ->
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
error = "搜尋失敗:${error.message}",
|
||||
searchResults = emptyList() // 失敗時清空列表
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue