From d9f5efc68071dff13e5c7b522c5eda46896d6b7b Mon Sep 17 00:00:00 2001 From: Eric85710 Date: Thu, 14 May 2026 13:37:39 +0800 Subject: [PATCH] auth to server-V95 --- .../auth/SwitchAccount/Add_Account_Page.kt | 271 ++++++++++++++++++ .../login_v3/navigation/MainViewModel.kt | 2 +- .../login_v3/navigation/nav_controller.kt | 7 +- 3 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/example/login_v3/auth/SwitchAccount/Add_Account_Page.kt diff --git a/app/src/main/java/com/example/login_v3/auth/SwitchAccount/Add_Account_Page.kt b/app/src/main/java/com/example/login_v3/auth/SwitchAccount/Add_Account_Page.kt new file mode 100644 index 0000000..a996e34 --- /dev/null +++ b/app/src/main/java/com/example/login_v3/auth/SwitchAccount/Add_Account_Page.kt @@ -0,0 +1,271 @@ +package com.example.login_v3.auth.SwitchAccount + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.AccountCircle +import androidx.compose.material.icons.rounded.Lock +import androidx.compose.material.icons.rounded.Visibility +import androidx.compose.material.icons.rounded.VisibilityOff +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.BlurredEdgeTreatment +import androidx.compose.ui.draw.blur +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Color.Companion.Red +import androidx.compose.ui.graphics.Color.Companion.Transparent +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import coil.ImageLoader +import coil.compose.AsyncImage +import coil.decode.SvgDecoder +import coil.request.ImageRequest +import com.example.login_v3.R +import com.example.login_v3.auth.login.AuthViewModel +import com.example.login_v3.auth.login.LoginUiState +import com.example.login_v3.ui.theme.light_orange +import com.example.login_v3.ui.theme.main_orange +import kotlin.text.ifEmpty + +@Composable +fun AddAccountScreen( + paddingValues: PaddingValues, + viewModel: AuthViewModel = hiltViewModel() +){ + + val uiState by viewModel.loginState.collectAsStateWithLifecycle() + val isLoading = uiState is LoginUiState.Loading + var username by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + var passwordVisible by remember { mutableStateOf(false) } + + var usernameError by remember { mutableStateOf("") } + var passwordError by remember { mutableStateOf("") } + + + + //background + Column( + modifier = Modifier + .fillMaxSize() + .background( + brush = Brush.linearGradient( + colors = listOf( + Color(0xFFDA7029), + Color(0xFF777777), + Color(0xFFB34800) + ) + ) + ) + .padding(paddingValues), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + + //title + Text(text = "Add Account", + fontSize = 32.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + + //login Box + Box( + modifier = Modifier + .height(340.dp) + .fillMaxWidth() + .padding(start = 24.dp, end = 24.dp, top = 20.dp), + contentAlignment = Alignment.Center + ){ + + + //glass effect + Box( + modifier = Modifier + .matchParentSize() + .clip(RoundedCornerShape(16.dp)) // 🔥 一定要有 + .blur( + radius = 10.dp, + edgeTreatment = BlurredEdgeTreatment.Unbounded + ) + .background( + Color.White.copy(alpha = 0.18f) + ) + .border( // ⭐ 玻璃邊框 + width = 1.dp, + color = Color.White.copy(alpha = 0.35f), + shape = RoundedCornerShape(16.dp) + ) + ) + + + + + + + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Spacer(modifier = Modifier.height(20.dp)) + + + TextField( + value = username, + onValueChange = { username = it}, + label = { + Text( + usernameError.ifEmpty { "username" }, + color = if (usernameError.isNotEmpty()) Red else Color.Unspecified + ) + }, + + leadingIcon = { + Icon( + Icons.Rounded.AccountCircle, + contentDescription = "" + ) + }, + shape = RoundedCornerShape(8.dp), + + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp, horizontal = 16.dp), + + colors = TextFieldDefaults.colors( + focusedContainerColor = Transparent, + unfocusedIndicatorColor = Transparent + ) + ) + + Spacer(modifier = Modifier.height(10.dp)) + + + TextField( + value = password, + onValueChange = { password = it }, + label = { + Text( + passwordError.ifEmpty { "Password" }, + color = if (passwordError.isNotEmpty()) Red else Color.Unspecified + ) + }, + leadingIcon = { + Icon(Icons.Rounded.Lock, contentDescription = "") + }, + + visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), + + trailingIcon = { + val image = if (passwordVisible) { + Icons.Rounded.Visibility + } else { + Icons.Rounded.VisibilityOff + } + + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon(imageVector = image, contentDescription = if (passwordVisible) "Hide password" else "Show password") + } + }, + + shape = RoundedCornerShape(8.dp), + + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp, horizontal = 16.dp), + + colors = TextFieldDefaults.colors( + focusedContainerColor = Transparent, + unfocusedIndicatorColor = Transparent + ) + ) + + Spacer(modifier = Modifier.height(20.dp)) + + + when (uiState) { + is LoginUiState.Success -> { + val user = (uiState as LoginUiState.Success).user + Text("歡迎,${user.display_name}") + } + is LoginUiState.Error -> { + Text(text = (uiState as LoginUiState.Error).message, color = Color.Red) + } + else -> {} + } + + + if (isLoading) { + CircularProgressIndicator(color = Color.White) + } else { + Button( + shape = RoundedCornerShape(8.dp), + onClick = { + usernameError = if(username.isBlank()) "Email is required" else "" + passwordError = if(password.isBlank()) "password is required" else "" + if (usernameError.isEmpty() && passwordError.isEmpty()){ + viewModel.login(username, password) + } + }, + colors = ButtonDefaults.buttonColors( + containerColor = main_orange, // 背景顏色 + contentColor = Color.White // 文字顏色 + ) + + ) { + Text( text = "Login") + } + } + + + + Spacer(modifier = Modifier.height(16.dp)) + + Text( text = "forget password?", + color = light_orange, + modifier = Modifier + .clickable{} + ) + } + + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/login_v3/navigation/MainViewModel.kt b/app/src/main/java/com/example/login_v3/navigation/MainViewModel.kt index 604575b..df9e4d5 100644 --- a/app/src/main/java/com/example/login_v3/navigation/MainViewModel.kt +++ b/app/src/main/java/com/example/login_v3/navigation/MainViewModel.kt @@ -32,7 +32,7 @@ class MainViewModel : ViewModel() { //init navigation enum class AppScreen { - PreReg, Login, Register, Home, ScreensTab, Loading, AccountSwitch + PreReg, Login, Register, Home, ScreensTab, Loading, AccountSwitch, AddAccount } @HiltViewModel diff --git a/app/src/main/java/com/example/login_v3/navigation/nav_controller.kt b/app/src/main/java/com/example/login_v3/navigation/nav_controller.kt index bc85de8..a5c870b 100644 --- a/app/src/main/java/com/example/login_v3/navigation/nav_controller.kt +++ b/app/src/main/java/com/example/login_v3/navigation/nav_controller.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.example.login_v3.auth.login.LoginScreen import com.example.login_v3.auth.Login_or_Reg_page import com.example.login_v3.auth.SwitchAccount.AccountSwitchScreen +import com.example.login_v3.auth.SwitchAccount.AddAccountScreen import com.example.login_v3.auth.reg.RegisterScreen import com.example.login_v3.auth.reg.Register_Screen import com.example.login_v3.home.HomeScreen @@ -45,6 +46,10 @@ fun AppNavGraph( paddingValues = paddingValues ) + AppScreen.AddAccount -> AddAccountScreen( + paddingValues = paddingValues + ) + AppScreen.Register -> { //back to pre_reg BackHandler { @@ -69,7 +74,7 @@ fun AppNavGraph( AppScreen.AccountSwitch -> { AccountSwitchScreen( // 點擊「登入另一個帳號」就跳轉到 Login 頁面 - onAddAccountClick = { appViewModel.goTo(AppScreen.Login) }, + onAddAccountClick = { appViewModel.goTo(AppScreen.AddAccount) }, // 點擊返回則回到原本的主分頁 (ScreensTab) onBack = { appViewModel.goTo(AppScreen.ScreensTab) } )