auth to server-V63
This commit is contained in:
parent
bd70b3e5e8
commit
6ceda89f04
6 changed files with 152 additions and 18 deletions
|
|
@ -45,10 +45,7 @@ interface TecnologiaApi {
|
||||||
@Part file: MultipartBody.Part
|
@Part file: MultipartBody.Part
|
||||||
): Response<Unit>
|
): Response<Unit>
|
||||||
|
|
||||||
// 發送好友請求
|
// contact
|
||||||
@POST("api/friends/request")
|
@GET("api/friends")
|
||||||
suspend fun sendFriendRequest(
|
suspend fun getFriends(): FriendListResponse
|
||||||
@Header("Authorization") token: String,
|
|
||||||
@Body body: FriendRequest
|
|
||||||
): Response<ApiResponse>
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,27 @@
|
||||||
package com.example.login_v3.data.api.api_class
|
package com.example.login_v3.data.api.api_class
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class FriendRequest(
|
data class FriendListResponse(
|
||||||
val friendId: String
|
val friends: List<Friend>
|
||||||
)
|
)
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class ApiResponse(
|
data class Friend(
|
||||||
val success: Boolean,
|
@Json(name = "friend_id")
|
||||||
val message: String
|
val friendId: String,
|
||||||
|
|
||||||
|
val username: String,
|
||||||
|
|
||||||
|
@Json(name = "display_name")
|
||||||
|
val displayName: String,
|
||||||
|
|
||||||
|
@Json(name = "avatar_url")
|
||||||
|
val avatarUrl: String?, // 建議給可空性,避免後端沒傳頭像時崩潰
|
||||||
|
|
||||||
|
val status: String,
|
||||||
|
|
||||||
|
@Json(name = "accepted_at")
|
||||||
|
val acceptedAt: String
|
||||||
)
|
)
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.example.login_v3.data.repository.basic
|
package com.example.login_v3.data.repository.basic
|
||||||
|
|
||||||
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.ApiResponse
|
|
||||||
import com.example.login_v3.data.api.api_class.LoginRequest
|
import com.example.login_v3.data.api.api_class.LoginRequest
|
||||||
import com.example.login_v3.data.api.api_class.LoginResponse
|
import com.example.login_v3.data.api.api_class.LoginResponse
|
||||||
import com.example.login_v3.data.api.api_class.RegisterRequest
|
import com.example.login_v3.data.api.api_class.RegisterRequest
|
||||||
|
|
@ -12,10 +11,6 @@ import javax.inject.Inject
|
||||||
class AuthRepository @Inject constructor(
|
class AuthRepository @Inject constructor(
|
||||||
private val api: TecnologiaApi
|
private val api: TecnologiaApi
|
||||||
) {
|
) {
|
||||||
/**
|
|
||||||
* 處理用戶註冊
|
|
||||||
* 回傳 Response<ApiResponse>,讓 ViewModel 可以根據 isSuccessful 判斷結果
|
|
||||||
*/
|
|
||||||
suspend fun register(request: RegisterRequest): Response<RegisterResponse> {
|
suspend fun register(request: RegisterRequest): Response<RegisterResponse> {
|
||||||
return api.register(request)
|
return api.register(request)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.example.login_v3.data.repository.basic
|
||||||
|
|
||||||
|
import com.example.login_v3.data.api.TecnologiaApi
|
||||||
|
import com.example.login_v3.data.api.api_class.Friend
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class FriendsRepository @Inject constructor
|
||||||
|
(private val apiService: TecnologiaApi) {
|
||||||
|
|
||||||
|
suspend fun getFriendList(): Result<List<Friend>> {
|
||||||
|
return try {
|
||||||
|
val response = apiService.getFriends()
|
||||||
|
// 回傳成功,並直接提取裡面的 List<Friend>
|
||||||
|
Result.success(response.friends)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// 處理連線超時、404、500 等各種錯誤
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,23 +1,93 @@
|
||||||
package com.example.login_v3.home.Message.UI.Detail
|
package com.example.login_v3.home.Message.UI.Detail
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import com.example.login_v3.R
|
||||||
|
import com.example.login_v3.data.api.api_class.Friend
|
||||||
|
import com.example.login_v3.home.Message.ViewModel.Detail.ContactListViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Message_contact_list(
|
fun Message_contact_list(
|
||||||
navController: NavController
|
navController: NavController,
|
||||||
|
viewModel: ContactListViewModel = hiltViewModel()
|
||||||
){
|
){
|
||||||
|
val friends by viewModel.friends.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
,
|
,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(text = "contact list")
|
if (friends.isEmpty()) {
|
||||||
|
// 如果列表是空的,顯示提示(或載入指示器)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize().padding(6.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text("目前沒有好友")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 顯示好友列表
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(6.dp)
|
||||||
|
) {
|
||||||
|
items(friends) { friend ->
|
||||||
|
FriendRow(friend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FriendRow(friend: Friend) {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = { Text(friend.displayName) },
|
||||||
|
supportingContent = { Text("@${friend.username}") },
|
||||||
|
leadingContent = {
|
||||||
|
// 使用 Coil 載入頭像
|
||||||
|
AsyncImage(
|
||||||
|
model = friend.avatarUrl,
|
||||||
|
contentDescription = "Avatar",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clip(CircleShape),
|
||||||
|
placeholder = painterResource(id = R.drawable.avatar_v1),
|
||||||
|
error = painterResource(id = R.drawable.avatar_v1)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailingContent = {
|
||||||
|
// 簡單顯示狀態
|
||||||
|
Text(
|
||||||
|
text = friend.status,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = if (friend.status == "online") Color.Green else Color.Gray
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.example.login_v3.home.Message.ViewModel.Detail
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.login_v3.data.api.api_class.Friend
|
||||||
|
import com.example.login_v3.data.repository.basic.FriendsRepository
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ContactListViewModel @Inject constructor
|
||||||
|
(
|
||||||
|
private val repository: FriendsRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _friends = MutableStateFlow<List<Friend>>(emptyList())
|
||||||
|
val friends: StateFlow<List<Friend>> = _friends
|
||||||
|
|
||||||
|
init {
|
||||||
|
// 畫面一建立就自動抓取資料
|
||||||
|
fetchFriends()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchFriends() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
repository.getFriendList()
|
||||||
|
.onSuccess { list ->
|
||||||
|
_friends.value = list
|
||||||
|
}
|
||||||
|
.onFailure {
|
||||||
|
// 這裡可以處理錯誤訊息,例如顯示 Toast
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue