auth to server-V47
This commit is contained in:
parent
36aa2835df
commit
03fe06ea27
3 changed files with 59 additions and 3 deletions
|
|
@ -3,6 +3,7 @@ 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.UserProfile
|
import com.example.login_v3.data.api.api_class.UserProfile
|
||||||
import com.example.login_v3.data.api.api_class.UserProfileUpdateRequest
|
import com.example.login_v3.data.api.api_class.UserProfileUpdateRequest
|
||||||
|
import okhttp3.MultipartBody
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class PersonalProfileRepository @Inject constructor(
|
class PersonalProfileRepository @Inject constructor(
|
||||||
|
|
@ -37,4 +38,18 @@ class PersonalProfileRepository @Inject constructor(
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. 上傳頭像圖片 (POST)
|
||||||
|
suspend fun uploadAvatar(avatarPart: MultipartBody.Part): Result<Unit> {
|
||||||
|
return try {
|
||||||
|
val response = api.uploadAvatar(avatarPart)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
Result.success(Unit)
|
||||||
|
} else {
|
||||||
|
Result.failure(Exception("圖片上傳失敗: ${response.code()}"))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import androidx.compose.ui.draw.blur
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -93,13 +94,13 @@ fun Loaded_setting_profile_page(
|
||||||
|
|
||||||
|
|
||||||
//image chooser
|
//image chooser
|
||||||
|
val context = LocalContext.current // 務必獲取 Context,uploadAvatar 需要它來解析 Uri
|
||||||
val launcher = rememberLauncherForActivityResult(
|
val launcher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.PickVisualMedia()
|
contract = ActivityResultContracts.PickVisualMedia()
|
||||||
) { uri ->
|
) { uri ->
|
||||||
// 2. 當使用者選完照片後,會回傳一個 Uri
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
// 直接把本地 Uri 轉成字串丟進去 (僅限本地 Demo,重整 App 後會失效)
|
// ✅ 修正:呼叫上傳圖片的函式,而不是更新網址字串的函式
|
||||||
viewModel.updateProfile(avatarUrl = uri.toString())
|
viewModel.uploadAvatar(context, uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package com.example.login_v3.home.setting.setting_detail_page.viewmodel
|
package com.example.login_v3.home.setting.setting_detail_page.viewmodel
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.example.login_v3.data.api.api_class.UserProfile
|
import com.example.login_v3.data.api.api_class.UserProfile
|
||||||
|
|
@ -10,6 +12,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// 定義 UI 狀態
|
// 定義 UI 狀態
|
||||||
|
|
@ -75,4 +80,39 @@ class PersonalProfileViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 在 ViewModel 類別內新增此 function
|
||||||
|
fun uploadAvatar(context: Context, uri: Uri) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
// 如果你想要在上傳時讓 UI 顯示 Loading,可以在這開啟
|
||||||
|
// _uiState.value = ProfileUiState.Loading
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 從 Uri 取得 InputStream 並讀取 Bytes
|
||||||
|
val inputStream = context.contentResolver.openInputStream(uri)
|
||||||
|
val bytes = inputStream?.use { it.readBytes() } ?: return@launch
|
||||||
|
|
||||||
|
// 2. 封裝成 RequestBody (指定為圖片格式)
|
||||||
|
val requestFile = bytes.toRequestBody("image/*".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
// 3. 建立 MultipartBody.Part
|
||||||
|
// 注意:"avatar" 必須跟後端 @Part("avatar") 裡面的名稱一模一樣
|
||||||
|
val body = MultipartBody.Part.createFormData("avatar", "avatar.jpg", requestFile)
|
||||||
|
|
||||||
|
// 4. 呼叫 Repository
|
||||||
|
repository.uploadAvatar(body)
|
||||||
|
.onSuccess {
|
||||||
|
// 上傳成功後,最保險的做法是重新 fetch 一次個人資料
|
||||||
|
// 這樣 UI 上的頭像網址才會更新成後端最新儲存的網址
|
||||||
|
fetchProfile()
|
||||||
|
}
|
||||||
|
.onFailure { e ->
|
||||||
|
// 處理錯誤,例如發送一個錯誤訊息給 UI
|
||||||
|
_uiState.value = ProfileUiState.Error(e.localizedMessage ?: "上傳失敗")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_uiState.value = ProfileUiState.Error("讀取檔案出錯: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue