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")
|
@GET("api/users/search")
|
||||||
suspend fun searchUsers(
|
suspend fun searchUsers(
|
||||||
@Query("q") query: String
|
@Query("q") query: String
|
||||||
): UserSearchResponse
|
): Response<UserSearchResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -85,18 +85,29 @@ data class AddFriendRequest(
|
||||||
)
|
)
|
||||||
|
|
||||||
//user search
|
//user search
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
data class UserSearchResponse(
|
data class UserSearchResponse(
|
||||||
val count: Int,
|
val count: Int,
|
||||||
val users: List<UserDetail>
|
val users: List<UserDetail>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true) // 內部類別也一定要加!
|
||||||
data class UserDetail(
|
data class UserDetail(
|
||||||
val id: String,
|
val id: String,
|
||||||
val username: String,
|
val username: String,
|
||||||
val display_name: String,
|
|
||||||
val avatar_url: String?, // 考慮到可能是 null,建議加上 ?
|
@Json(name = "display_name") // 這樣你的 Kotlin 代碼可以用漂亮的 displayName
|
||||||
val banner_url: String?,
|
val displayName: String,
|
||||||
|
|
||||||
|
@Json(name = "avatar_url")
|
||||||
|
val avatarUrl: String?,
|
||||||
|
|
||||||
|
@Json(name = "banner_url")
|
||||||
|
val bannerUrl: String?,
|
||||||
|
|
||||||
val bio: String?,
|
val bio: String?,
|
||||||
val status: 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.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.api.api_class.UserDetail
|
||||||
|
import com.example.login_v3.data.api.api_class.UserSearchResponse
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -58,6 +59,34 @@ class FriendsRepository @Inject constructor(
|
||||||
return result
|
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 函式
|
// 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 {
|
||||||
|
|
@ -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) {
|
fun onSearchQueryChanged(newQuery: String) {
|
||||||
// 更新 UI 上的輸入文字
|
// 1. 即時更新文字輸入,確保 UI TextField 顯示順暢
|
||||||
_uiState.update { it.copy(searchQuery = newQuery) }
|
_uiState.update { it.copy(searchQuery = newQuery) }
|
||||||
|
}
|
||||||
|
|
||||||
// 如果關鍵字為空,清空搜尋結果並返回
|
fun performSearch() {
|
||||||
if (newQuery.isBlank()) {
|
val query = _uiState.value.searchQuery
|
||||||
|
if (query.isBlank()) {
|
||||||
|
// 如果關鍵字為空,清空搜尋結果
|
||||||
_uiState.update { it.copy(searchResults = emptyList()) }
|
_uiState.update { it.copy(searchResults = emptyList()) }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 防抖處理 (Debounce) ---
|
viewModelScope.launch {
|
||||||
searchJob?.cancel() // 取消前一個還在跑的搜尋
|
// 2. 顯示 Loading
|
||||||
searchJob = viewModelScope.launch {
|
_uiState.update { it.copy(isLoading = true, error = null) }
|
||||||
delay(500) // 使用者停頓 0.5 秒後才真正發出請求
|
|
||||||
performSearch(newQuery)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun performSearch(query: String) {
|
// 3. 呼叫 Repository
|
||||||
_uiState.update { it.copy(isLoading = true) }
|
val result = repository.searchUsers(query)
|
||||||
|
|
||||||
val result = repository.searchUsers(query)
|
// 4. 處理結果
|
||||||
|
result.onSuccess { response ->
|
||||||
result.onSuccess { users ->
|
_uiState.update {
|
||||||
_uiState.update { it.copy(
|
it.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
searchResults = users,
|
searchResults = response.users // UserSearchResponse 裡的列表
|
||||||
error = null
|
)
|
||||||
)}
|
}
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
_uiState.update { it.copy(
|
_uiState.update {
|
||||||
isLoading = false,
|
it.copy(
|
||||||
error = "搜尋失敗: ${error.message}"
|
isLoading = false,
|
||||||
)}
|
error = "搜尋失敗:${error.message}",
|
||||||
|
searchResults = emptyList() // 失敗時清空列表
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue