重构登录注册页面UI
- 提取ActionButton组件以复用 - 提取TextInputField组件以复用 - 更新Profile页面布局 - 更新ChangePasswordScreen页面布局 - 更新EmailSignup页面布局 - 更新UserAuth页面布局 - 更新Signup页面布局 - 更新Login页面布局
@@ -1,12 +1,30 @@
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.foundation.layout.Column
|
||||||
|
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.width
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
|
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||||
|
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||||
|
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||||
|
import com.aiosman.riderpro.ui.composables.TextInputField
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,60 +55,93 @@ fun ChangePasswordScreen() {
|
|||||||
var errorMessage by remember { mutableStateOf("") }
|
var errorMessage by remember { mutableStateOf("") }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
Scaffold { paddingValues ->
|
var oldPasswordError by remember { mutableStateOf<String?>(null) }
|
||||||
Column(
|
var confirmPasswordError by remember { mutableStateOf<String?>(null) }
|
||||||
modifier = Modifier
|
var passwordError by remember { mutableStateOf<String?>(null) }
|
||||||
.fillMaxSize()
|
fun validate(): Boolean {
|
||||||
.padding(paddingValues),
|
oldPasswordError =
|
||||||
verticalArrangement = Arrangement.Center
|
if (currentPassword.isEmpty()) "Please enter your current password" else null
|
||||||
|
passwordError = when {
|
||||||
|
newPassword.length < 8 -> "Password must be at least 8 characters long"
|
||||||
|
!newPassword.any { it.isDigit() } -> "Password must contain at least one digit"
|
||||||
|
!newPassword.any { it.isUpperCase() } -> "Password must contain at least one uppercase letter"
|
||||||
|
!newPassword.any { it.isLowerCase() } -> "Password must contain at least one lowercase letter"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
confirmPasswordError =
|
||||||
|
if (newPassword != confirmPassword) "Passwords do not match" else null
|
||||||
|
return passwordError == null && confirmPasswordError == null && oldPasswordError == null
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.White),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
StatusBarSpacer()
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp)
|
||||||
) {
|
) {
|
||||||
TextField(
|
NoticeScreenHeader(
|
||||||
value = currentPassword,
|
title = "Change password",
|
||||||
|
moreIcon = false
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(80.dp))
|
||||||
|
TextInputField(
|
||||||
|
text = currentPassword,
|
||||||
onValueChange = { currentPassword = it },
|
onValueChange = { currentPassword = it },
|
||||||
label = { Text("Current Password") },
|
password = true,
|
||||||
visualTransformation = PasswordVisualTransformation(),
|
label = "Current password",
|
||||||
modifier = Modifier.fillMaxWidth()
|
hint = "Enter your current password",
|
||||||
|
error = oldPasswordError
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
TextField(
|
TextInputField(
|
||||||
value = newPassword,
|
text = newPassword,
|
||||||
onValueChange = { newPassword = it },
|
onValueChange = { newPassword = it },
|
||||||
label = { Text("New Password") },
|
password = true,
|
||||||
visualTransformation = PasswordVisualTransformation(),
|
label = "New password",
|
||||||
modifier = Modifier.fillMaxWidth()
|
hint = "Enter your new password",
|
||||||
|
error = passwordError
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
TextField(
|
TextInputField(
|
||||||
value = confirmPassword,
|
text = confirmPassword,
|
||||||
onValueChange = { confirmPassword = it },
|
onValueChange = { confirmPassword = it },
|
||||||
label = { Text("Confirm New Password") },
|
password = true,
|
||||||
visualTransformation = PasswordVisualTransformation(),
|
label = "Confirm new password",
|
||||||
modifier = Modifier.fillMaxWidth()
|
hint = "Enter your new password again",
|
||||||
|
error = confirmPasswordError
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(80.dp))
|
||||||
if (errorMessage.isNotEmpty()) {
|
ActionButton(
|
||||||
Text(
|
modifier = Modifier
|
||||||
text = errorMessage,
|
.width(345.dp)
|
||||||
color = MaterialTheme.colorScheme.error,
|
.height(48.dp),
|
||||||
modifier = Modifier.padding(bottom = 16.dp)
|
text = "LET'S RIDE".uppercase(),
|
||||||
)
|
backgroundImage = R.mipmap.rider_pro_signup_red_bg
|
||||||
}
|
) {
|
||||||
Button(
|
if (validate()) {
|
||||||
onClick = {
|
scope.launch {
|
||||||
if (newPassword == confirmPassword) {
|
try {
|
||||||
scope.launch {
|
|
||||||
viewModel.changePassword(currentPassword, newPassword)
|
viewModel.changePassword(currentPassword, newPassword)
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
errorMessage = e.message ?: "An error occurred"
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
errorMessage = "Passwords do not match"
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text("Change Password")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.aiosman.riderpro.ui.composables
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ActionButton(
|
||||||
|
modifier: Modifier,
|
||||||
|
text: String,
|
||||||
|
color: Color = Color.White,
|
||||||
|
@DrawableRes backgroundImage: Int,
|
||||||
|
leading: @Composable (() -> Unit)? = null,
|
||||||
|
click: () -> Unit = {}
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier.noRippleClickable {
|
||||||
|
click()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = backgroundImage),
|
||||||
|
contentDescription = "Rider Pro",
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentScale = ContentScale.FillBounds
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
leading?.invoke()
|
||||||
|
Text(text, fontSize = 16.sp, color = color, fontWeight = FontWeight.W400)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package com.aiosman.riderpro.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.focus.onFocusChanged
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
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 com.aiosman.riderpro.R
|
||||||
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TextInputField(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
text: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
password: Boolean = false,
|
||||||
|
label: String? = null,
|
||||||
|
hint: String? = null,
|
||||||
|
error: String? = null
|
||||||
|
) {
|
||||||
|
var showPassword by remember { mutableStateOf(!password) }
|
||||||
|
var isFocused by remember { mutableStateOf(false) }
|
||||||
|
Column(modifier = modifier) {
|
||||||
|
label?.let {
|
||||||
|
Text(it, color = Color(0xFFCCCCCC))
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
BasicTextField(
|
||||||
|
value = text,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.onFocusChanged { focusState ->
|
||||||
|
isFocused = focusState.isFocused
|
||||||
|
},
|
||||||
|
textStyle = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.W500,
|
||||||
|
color = Color(0xff333333)
|
||||||
|
),
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = if (password) KeyboardType.Password else KeyboardType.Text
|
||||||
|
),
|
||||||
|
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
|
||||||
|
singleLine = true,
|
||||||
|
)
|
||||||
|
if (password) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.rider_pro_eye),
|
||||||
|
contentDescription = "Password",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
showPassword = !showPassword
|
||||||
|
},
|
||||||
|
colorFilter = ColorFilter.tint(Color.Black)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
hint?.let {
|
||||||
|
Text(it, color = Color(0xFFCCCCCC), fontWeight = FontWeight.W600)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(1.dp)
|
||||||
|
.background(if (isFocused) Color(0xff333333) else Color(0xFFF5F5F5))
|
||||||
|
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
if (error != null) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.mipmap.rider_pro_input_error),
|
||||||
|
contentDescription = "Error",
|
||||||
|
modifier = Modifier.size(8.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.size(4.dp))
|
||||||
|
Text(error, color = Color(0xFFE53935), fontSize = 12.sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,8 +95,12 @@ fun ProfilePage() {
|
|||||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize().background(Color(0xFFF5F5F5)).padding(bottom = with(LocalDensity.current) {
|
.fillMaxSize()
|
||||||
val da = WindowInsets.navigationBars.getBottom(this).toDp() + 48.dp
|
.background(Color(0xFFF5F5F5))
|
||||||
|
.padding(bottom = with(LocalDensity.current) {
|
||||||
|
val da = WindowInsets.navigationBars
|
||||||
|
.getBottom(this)
|
||||||
|
.toDp() + 48.dp
|
||||||
da
|
da
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
@@ -118,7 +122,7 @@ fun ProfilePage() {
|
|||||||
contentScale = ContentScale.Crop
|
contentScale = ContentScale.Crop
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
|
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -145,44 +149,60 @@ fun ProfilePage() {
|
|||||||
},
|
},
|
||||||
tint = Color.White
|
tint = Color.White
|
||||||
)
|
)
|
||||||
MaterialTheme(shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(16.dp))) {
|
MaterialTheme(
|
||||||
|
shapes = MaterialTheme.shapes.copy(
|
||||||
|
extraSmall = RoundedCornerShape(
|
||||||
|
16.dp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onDismissRequest = { expanded = false },
|
onDismissRequest = { expanded = false },
|
||||||
modifier = Modifier.width(250.dp).background(Color.White)
|
modifier = Modifier
|
||||||
|
.width(250.dp)
|
||||||
|
.background(Color.White)
|
||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.padding(vertical = 14.dp, horizontal = 24.dp)) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 14.dp, horizontal = 24.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
expanded = false
|
||||||
|
scope.launch {
|
||||||
|
model.logout()
|
||||||
|
navController.navigate(NavigationRoute.Login.route) {
|
||||||
|
popUpTo(NavigationRoute.Index.route) {
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) {
|
||||||
Row {
|
Row {
|
||||||
Text("Logout", fontWeight = FontWeight.W500)
|
Text("Logout", fontWeight = FontWeight.W500)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.mipmap.rider_pro_logout),
|
painter = painterResource(id = R.mipmap.rider_pro_logout),
|
||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
modifier = Modifier.size(24.dp).noRippleClickable {
|
modifier = Modifier.size(24.dp)
|
||||||
scope.launch {
|
|
||||||
model.logout()
|
|
||||||
navController.navigate(NavigationRoute.Login.route) {
|
|
||||||
popUpTo(NavigationRoute.Index.route) {
|
|
||||||
inclusive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(modifier = Modifier.padding(vertical = 14.dp, horizontal = 24.dp)) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 14.dp, horizontal = 24.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
expanded = false
|
||||||
|
scope.launch {
|
||||||
|
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
Row {
|
Row {
|
||||||
Text("Change password",fontWeight = FontWeight.W500)
|
Text("Change password", fontWeight = FontWeight.W500)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.mipmap.rider_pro_change_password),
|
painter = painterResource(id = R.mipmap.rider_pro_change_password),
|
||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
modifier = Modifier.size(24.dp).noRippleClickable {
|
modifier = Modifier.size(24.dp)
|
||||||
scope.launch {
|
|
||||||
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,7 +464,9 @@ fun CommunicationOperatorGroup(
|
|||||||
contentDescription = ""
|
contentDescription = ""
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(R.string.follow_upper),
|
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(
|
||||||
|
R.string.follow_upper
|
||||||
|
),
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||||
@@ -565,7 +587,9 @@ fun TimeGroup(time: String = "2024.06.08 12:23") {
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
modifier = Modifier.height(16.dp).width(14.dp),
|
modifier = Modifier
|
||||||
|
.height(16.dp)
|
||||||
|
.width(14.dp),
|
||||||
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag),
|
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag),
|
||||||
contentDescription = ""
|
contentDescription = ""
|
||||||
)
|
)
|
||||||
@@ -654,7 +678,7 @@ fun MomentCardPicture(imageUrl: String, momentEntity: MomentEntity) {
|
|||||||
imageUrl,
|
imageUrl,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.aspectRatio(3f/2f)
|
.aspectRatio(3f / 2f)
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
PostViewModel.preTransit(momentEntity)
|
PostViewModel.preTransit(momentEntity)
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ import com.aiosman.riderpro.data.ServiceException
|
|||||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||||
|
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
|
import com.aiosman.riderpro.ui.composables.TextInputField
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.aiosman.riderpro.ui.login
|
package com.aiosman.riderpro.ui.login
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -18,18 +17,14 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
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
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavOptions
|
|
||||||
import androidx.navigation.navOptions
|
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginPage() {
|
fun LoginPage() {
|
||||||
@@ -106,33 +101,3 @@ fun LoginPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ActionButton(
|
|
||||||
modifier: Modifier,
|
|
||||||
text: String,
|
|
||||||
color: Color = Color.White,
|
|
||||||
@DrawableRes backgroundImage: Int,
|
|
||||||
leading: @Composable (() -> Unit)? = null,
|
|
||||||
click: () -> Unit = {}
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = modifier.noRippleClickable {
|
|
||||||
click()
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = backgroundImage),
|
|
||||||
contentDescription = "Rider Pro",
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentScale = ContentScale.FillBounds
|
|
||||||
)
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
leading?.invoke()
|
|
||||||
Text(text, fontSize = 16.sp, color = color, fontWeight = FontWeight.W400)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,25 +15,18 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
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.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
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
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.credentials.Credential
|
|
||||||
import androidx.credentials.CredentialManager
|
|
||||||
import androidx.credentials.CustomCredential
|
|
||||||
import androidx.credentials.GetCredentialRequest
|
|
||||||
import androidx.credentials.GetCredentialResponse
|
|
||||||
import com.aiosman.riderpro.AppStore
|
import com.aiosman.riderpro.AppStore
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.Messaging
|
import com.aiosman.riderpro.Messaging
|
||||||
@@ -42,6 +35,7 @@ import com.aiosman.riderpro.data.AccountService
|
|||||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import com.aiosman.riderpro.data.ServiceException
|
import com.aiosman.riderpro.data.ServiceException
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
|
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.aiosman.riderpro.utils.GoogleLogin
|
import com.aiosman.riderpro.utils.GoogleLogin
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.aiosman.riderpro.ui.login
|
|||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -14,8 +13,6 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.CheckboxDefaults
|
import androidx.compose.material3.CheckboxDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -31,13 +28,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.credentials.CredentialManager
|
import androidx.credentials.CredentialManager
|
||||||
import com.aiosman.riderpro.AppStore
|
import com.aiosman.riderpro.AppStore
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
@@ -48,7 +39,9 @@ import com.aiosman.riderpro.data.AccountServiceImpl
|
|||||||
import com.aiosman.riderpro.data.ServiceException
|
import com.aiosman.riderpro.data.ServiceException
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||||
|
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
|
import com.aiosman.riderpro.ui.composables.TextInputField
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.aiosman.riderpro.utils.GoogleLogin
|
import com.aiosman.riderpro.utils.GoogleLogin
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -227,66 +220,3 @@ fun UserAuthScreen() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun TextInputField(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
text: String,
|
|
||||||
onValueChange: (String) -> Unit,
|
|
||||||
password: Boolean = false,
|
|
||||||
label: String? = null,
|
|
||||||
hint: String? = null
|
|
||||||
|
|
||||||
) {
|
|
||||||
var showPassword by remember { mutableStateOf(!password) }
|
|
||||||
Column(modifier = modifier) {
|
|
||||||
label?.let {
|
|
||||||
Text(it, color = Color(0xFFCCCCCC))
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
}
|
|
||||||
Box(
|
|
||||||
contentAlignment = Alignment.CenterStart
|
|
||||||
) {
|
|
||||||
Row {
|
|
||||||
BasicTextField(
|
|
||||||
value = text,
|
|
||||||
onValueChange = onValueChange,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
textStyle = TextStyle(
|
|
||||||
fontSize = 16.sp,
|
|
||||||
fontWeight = FontWeight.W500
|
|
||||||
),
|
|
||||||
keyboardOptions = KeyboardOptions(
|
|
||||||
keyboardType = if (password) KeyboardType.Password else KeyboardType.Text
|
|
||||||
),
|
|
||||||
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
|
|
||||||
singleLine = true
|
|
||||||
)
|
|
||||||
if (password) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = R.drawable.rider_pro_eye),
|
|
||||||
contentDescription = "Password",
|
|
||||||
modifier = Modifier
|
|
||||||
.size(24.dp)
|
|
||||||
.noRippleClickable {
|
|
||||||
showPassword = !showPassword
|
|
||||||
},
|
|
||||||
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.Black)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
hint?.let {
|
|
||||||
Text(it, color = Color(0xFFCCCCCC), fontWeight = FontWeight.W600)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(1.dp)
|
|
||||||
.background(Color(0xFFF5F5F5))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
app/src/main/res/mipmap-hdpi/rider_pro_btn_bg_grey.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/mipmap-hdpi/rider_pro_change_password.png
Normal file
|
After Width: | Height: | Size: 708 B |
BIN
app/src/main/res/mipmap-hdpi/rider_pro_input_error.png
Normal file
|
After Width: | Height: | Size: 333 B |
BIN
app/src/main/res/mipmap-hdpi/rider_pro_logout.png
Normal file
|
After Width: | Height: | Size: 909 B |
BIN
app/src/main/res/mipmap-mdpi/rider_pro_btn_bg_grey.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-mdpi/rider_pro_change_password.png
Normal file
|
After Width: | Height: | Size: 445 B |
BIN
app/src/main/res/mipmap-mdpi/rider_pro_input_error.png
Normal file
|
After Width: | Height: | Size: 197 B |
BIN
app/src/main/res/mipmap-mdpi/rider_pro_logout.png
Normal file
|
After Width: | Height: | Size: 520 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_btn_bg_grey.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_change_password.png
Normal file
|
After Width: | Height: | Size: 844 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_input_error.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
app/src/main/res/mipmap-xhdpi/rider_pro_logout.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/rider_pro_btn_bg_grey.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/rider_pro_change_password.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/rider_pro_input_error.png
Normal file
|
After Width: | Height: | Size: 640 B |
BIN
app/src/main/res/mipmap-xxhdpi/rider_pro_logout.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/rider_pro_btn_bg_grey.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/rider_pro_change_password.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/rider_pro_input_error.png
Normal file
|
After Width: | Height: | Size: 700 B |
BIN
app/src/main/res/mipmap-xxxhdpi/rider_pro_logout.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |