auth to server-V63

This commit is contained in:
Eric85710 2026-05-06 17:07:54 +08:00
parent bd70b3e5e8
commit 6ceda89f04
6 changed files with 152 additions and 18 deletions

View file

@ -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>
} }

View file

@ -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
) )

View file

@ -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)
} }

View file

@ -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)
}
}
}

View file

@ -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
)
}
)
} }

View file

@ -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
}
}
}
}