From 03fe06ea27f94e4671811dc2e0e20d1df5f54006 Mon Sep 17 00:00:00 2001 From: Eric85710 Date: Mon, 4 May 2026 06:50:01 +0800 Subject: [PATCH] auth to server-V47 --- .../basic/Personal_Profile_Repository.kt | 15 +++++++ .../detail_UI/setting_Profile_Page.kt | 7 ++-- .../viewmodel/Profile_Viewmodel.kt | 40 +++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/example/login_v3/data/repository/basic/Personal_Profile_Repository.kt b/app/src/main/java/com/example/login_v3/data/repository/basic/Personal_Profile_Repository.kt index 8c335c9..528f058 100644 --- a/app/src/main/java/com/example/login_v3/data/repository/basic/Personal_Profile_Repository.kt +++ b/app/src/main/java/com/example/login_v3/data/repository/basic/Personal_Profile_Repository.kt @@ -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.api_class.UserProfile import com.example.login_v3.data.api.api_class.UserProfileUpdateRequest +import okhttp3.MultipartBody import javax.inject.Inject class PersonalProfileRepository @Inject constructor( @@ -37,4 +38,18 @@ class PersonalProfileRepository @Inject constructor( Result.failure(e) } } + + // 3. 上傳頭像圖片 (POST) + suspend fun uploadAvatar(avatarPart: MultipartBody.Part): Result { + 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) + } + } } diff --git a/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/detail_UI/setting_Profile_Page.kt b/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/detail_UI/setting_Profile_Page.kt index 52adb6d..a75a2e0 100644 --- a/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/detail_UI/setting_Profile_Page.kt +++ b/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/detail_UI/setting_Profile_Page.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.draw.blur import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -93,13 +94,13 @@ fun Loaded_setting_profile_page( //image chooser + val context = LocalContext.current // 務必獲取 Context,uploadAvatar 需要它來解析 Uri val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickVisualMedia() ) { uri -> - // 2. 當使用者選完照片後,會回傳一個 Uri if (uri != null) { - // 直接把本地 Uri 轉成字串丟進去 (僅限本地 Demo,重整 App 後會失效) - viewModel.updateProfile(avatarUrl = uri.toString()) + // ✅ 修正:呼叫上傳圖片的函式,而不是更新網址字串的函式 + viewModel.uploadAvatar(context, uri) } } diff --git a/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/viewmodel/Profile_Viewmodel.kt b/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/viewmodel/Profile_Viewmodel.kt index 14199f6..714f92a 100644 --- a/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/viewmodel/Profile_Viewmodel.kt +++ b/app/src/main/java/com/example/login_v3/home/setting/setting_detail_page/viewmodel/Profile_Viewmodel.kt @@ -1,5 +1,7 @@ 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.viewModelScope 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.asStateFlow import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.toRequestBody import javax.inject.Inject // 定義 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}") + } + } + } + } \ No newline at end of file