更新登录UI

This commit is contained in:
2024-09-30 22:14:52 +08:00
parent 2c6af2ad94
commit 1935da7e72
10 changed files with 289 additions and 116 deletions

View File

@@ -145,10 +145,9 @@ fun ResetPasswordScreen() {
TextInputField(
text = username,
onValueChange = { username = it },
label = stringResource(R.string.login_email_label),
hint = stringResource(R.string.text_hint_email),
enabled = !isLoading,
error = usernameError
error = usernameError,
)
Spacer(modifier = Modifier.height(72.dp))
if (isLoading) {
@@ -159,7 +158,8 @@ fun ResetPasswordScreen() {
.width(345.dp)
.height(48.dp),
text = stringResource(R.string.recover),
backgroundImage = R.mipmap.rider_pro_signup_red_bg
backgroundColor = Color(0xffda3832),
) {
resetPassword()
}

View File

@@ -1,19 +1,21 @@
package com.aiosman.riderpro.ui.composables
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
@@ -21,28 +23,38 @@ import com.aiosman.riderpro.ui.modifiers.noRippleClickable
fun ActionButton(
modifier: Modifier,
text: String,
color: Color = Color.White,
@DrawableRes backgroundImage: Int,
color: Color = Color.Black,
@DrawableRes backgroundImage: Int? = null,
backgroundColor: Color = Color(0xfff0f0f0),
leading: @Composable (() -> Unit)? = null,
expandText: Boolean = false,
contentPadding: PaddingValues = PaddingValues(vertical = 16.dp),
click: () -> Unit = {}
) {
Box(
modifier = modifier.noRippleClickable {
click()
}
modifier = modifier
.clip(RoundedCornerShape(24.dp))
.background(backgroundColor)
.padding(contentPadding)
.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 = 14.sp, color = color, fontWeight = FontWeight.W600, fontStyle = FontStyle.Italic)
Text(
text,
fontSize = 14.sp,
color = color,
fontWeight = FontWeight.W600,
modifier = Modifier.let {
if (expandText) it.weight(1f) else it
},
textAlign = if (expandText) TextAlign.Center else TextAlign.Start
)
}
}

View File

@@ -7,12 +7,14 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
@@ -34,8 +36,9 @@ fun Checkbox(
.noRippleClickable {
onCheckedChange(!checked)
}
.clip(CircleShape)
.background(color = backgroundColor)
.border(width = borderWidth, color = borderColor)
.border(width = borderWidth, color = borderColor, shape = CircleShape)
.padding(2.dp)
) {
if (checked) {

View File

@@ -6,13 +6,16 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
@@ -25,6 +28,7 @@ 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.clip
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
@@ -58,7 +62,16 @@ fun TextInputField(
Spacer(modifier = Modifier.height(16.dp))
}
Box(
contentAlignment = Alignment.CenterStart
contentAlignment = Alignment.CenterStart,
modifier = Modifier
.clip(RoundedCornerShape(24.dp))
.background(Color(0xFFF7f7f7))
.border(
width = 1.dp,
color = if (error == null) Color.Transparent else Color(0xFFE53935),
shape = RoundedCornerShape(24.dp)
)
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
Row {
BasicTextField(
@@ -97,18 +110,10 @@ fun TextInputField(
if (text.isEmpty()) {
hint?.let {
Text(it, color = Color(0xFFCCCCCC), fontWeight = FontWeight.W600)
Text(it, color = Color(0xffdadada), 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

View File

@@ -54,6 +54,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
@@ -137,7 +138,7 @@ fun Profile(
var headerBannerMaxHeight: Int = userHeight + bannerHeight
val headerBannerMinHeight = 100
val speedFactor = 0.5f
var currentHeaderHeight by rememberSaveable{ mutableStateOf<Int>(headerBannerMaxHeight) }
var currentHeaderHeight by rememberSaveable { mutableStateOf<Int>(headerBannerMaxHeight) }
var scrollState = rememberLazyListState()
var gridScrollState = rememberLazyStaggeredGridState()
var pagerState = rememberPagerState(pageCount = { 2 })
@@ -332,13 +333,15 @@ fun Profile(
}
}
// user info
Column(
modifier = Modifier
.fillMaxWidth()
.height(if (currentHeaderHeight.dp > bannerHeight.dp) userHeight.dp else currentHeaderHeight.dp)
Box(
modifier = Modifier.fillMaxWidth()
) {
if (currentHeaderHeight.dp > headerBannerMinHeight.dp) {
// user info
Column(
modifier = Modifier
.fillMaxWidth()
.height(if (currentHeaderHeight.dp > bannerHeight.dp) userHeight.dp else currentHeaderHeight.dp)
) {
Spacer(modifier = Modifier.height(16.dp))
// 个人信息
Box(
@@ -371,14 +374,35 @@ fun Profile(
}
}
// collapsed bar
}
val thresholdHeight = 200 // 设置阈值高度
val startChangeHeight = headerBannerMinHeight + thresholdHeight
val alpha = if (currentHeaderHeight < startChangeHeight) {
((currentHeaderHeight - headerBannerMinHeight).toFloat() / thresholdHeight.toFloat()).coerceIn(
0f,
1f
)
} else {
1f // 高度大于阈值时alpha 为 0
}
Column(
modifier = Modifier
.fillMaxWidth()
// 保持在最低高度和当前高度之间
.alpha(1 - alpha)
.background(Color(0xfff8f8f8))
.padding(horizontal = 16.dp)
) {
StatusBarSpacer()
Row(
modifier = Modifier
.fillMaxWidth()
.height(headerBannerMinHeight.dp)
// .background(Color(0xfff8f8f8))
.padding(horizontal = 16.dp),
modifier = Modifier.height(headerBannerMinHeight.dp),
verticalAlignment = Alignment.CenterVertically
) {
CustomAsyncImage(
@@ -398,10 +422,10 @@ fun Profile(
color = Color.Black
)
}
Spacer(modifier = Modifier.height(currentHeaderHeight.dp - headerBannerMinHeight.dp))
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Row(
@@ -512,7 +536,9 @@ fun Profile(
Box(
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
.aspectRatio(
if (idx % 3 == 0) 1.5f else 1f
)
.clip(RoundedCornerShape(8.dp))
.noRippleClickable {
navController.navigateToPost(

View File

@@ -1,7 +1,5 @@
package com.aiosman.riderpro.ui.login
import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -28,7 +26,6 @@ import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.ErrorCode
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.Messaging
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.ServiceException
@@ -41,7 +38,6 @@ import com.aiosman.riderpro.ui.composables.CheckboxWithLabel
import com.aiosman.riderpro.ui.composables.PolicyCheckbox
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
import com.aiosman.riderpro.ui.composables.TextInputField
import com.aiosman.riderpro.utils.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -182,7 +178,7 @@ fun EmailSignupScreen() {
.fillMaxWidth()
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
) {
NoticeScreenHeader(stringResource(R.string.sign_in_upper), moreIcon = false)
NoticeScreenHeader(stringResource(R.string.sign_up_upper), moreIcon = false)
}
Spacer(modifier = Modifier.padding(32.dp))
Column(
@@ -198,7 +194,6 @@ fun EmailSignupScreen() {
onValueChange = {
email = it
},
label = stringResource(R.string.login_email_label),
hint = stringResource(R.string.text_hint_email),
error = emailError
)
@@ -211,7 +206,6 @@ fun EmailSignupScreen() {
password = it
},
password = true,
label = stringResource(R.string.login_password_label),
hint = stringResource(R.string.text_hint_password),
error = passwordError
)
@@ -224,7 +218,6 @@ fun EmailSignupScreen() {
confirmPassword = it
},
password = true,
label = stringResource(R.string.login_confirm_password_label),
hint = stringResource(R.string.text_hint_confirm_password),
error = confirmPasswordError
)
@@ -279,7 +272,8 @@ fun EmailSignupScreen() {
.width(345.dp)
.height(48.dp),
text = stringResource(R.string.lets_ride_upper),
backgroundImage = R.mipmap.rider_pro_signup_red_bg
backgroundColor = Color(0xffda3832),
color = Color.White
) {
scope.launch(Dispatchers.IO) {
registerUser()

View File

@@ -1,104 +1,241 @@
package com.aiosman.riderpro.ui.login
import android.content.ContentValues.TAG
import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
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.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.ServiceException
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.utils.GoogleLogin
import com.google.accompanist.systemuicontroller.SystemUiController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun LoginPage() {
val navController = LocalNavController.current
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val accountService = AccountServiceImpl()
val statusBarController = rememberSystemUiController()
LaunchedEffect(Unit) {
// navController.navigate(NavigationRoute.Index.route)
statusBarController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Scaffold {
it
fun googleLogin() {
coroutineScope.launch {
try {
GoogleLogin(context) {
coroutineScope.launch {
try {
accountService.regiterUserWithGoogleAccount(it)
} catch (e: Exception) {
Log.e(TAG, "Failed to register with google", e)
return@launch
}
// 获取用户信息
// 获取 token
val authResp = accountService.loginUserWithGoogle(it)
if (authResp.token != null) {
coroutineScope.launch(Dispatchers.Main) {
Toast.makeText(
context,
"Successfully registered",
Toast.LENGTH_SHORT
)
.show()
}
}
AppStore.apply {
token = authResp.token
this.rememberMe = true
saveData()
}
// 获取token 信息
try {
AppState.initWithAccount(coroutineScope, context)
} catch (e: Exception) {
Log.e(TAG, "Failed to init with account", e)
} catch (e: ServiceException) {
coroutineScope.launch(Dispatchers.Main) {
Toast.makeText(context, "Failed to get account", Toast.LENGTH_SHORT)
.show()
}
}
coroutineScope.launch(Dispatchers.Main) {
navController.navigate(NavigationRoute.Index.route) {
popUpTo(NavigationRoute.Login.route) { inclusive = true }
}
}
}
}
} catch (e: Exception) {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}
}
}
Box(
modifier = Modifier.fillMaxSize().background(Color.White)
) {
// bg image
Box(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxWidth().height(900.dp).offset(
y = (-72).dp
),
) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_login_bg),
contentDescription = "Login Background",
modifier = Modifier.fillMaxWidth().fillMaxHeight(),
contentScale = androidx.compose.ui.layout.ContentScale.Crop,
)
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(Color.White.copy(alpha = 0.8f)),
contentAlignment = Alignment.BottomCenter
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.White
),
startY = 0f,
endY = 300f
)
)
)
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
) {
Spacer(modifier = Modifier.weight(1f))
// to bottom
Box(
contentAlignment = Alignment.TopCenter,
modifier = Modifier.padding(top = 211.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_logo),
painter = painterResource(id = R.mipmap.rider_pro_color_logo),
contentDescription = "Rider Pro",
modifier = Modifier
.width(108.dp)
.height(45.dp)
.size(48.dp)
)
Spacer(modifier = Modifier.height(32.dp))
Text(
"Connecting Riders".uppercase(),
fontSize = 28.sp,
fontWeight = FontWeight.Bold
fontWeight = FontWeight.W600
)
Text("Worldwide".uppercase(), fontSize = 28.sp, fontWeight = FontWeight.Bold)
Text("Worldwide".uppercase(), fontSize = 28.sp, fontWeight = FontWeight.W600)
}
}
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 82.dp + 162.dp)
Spacer(modifier = Modifier.height(32.dp))
ActionButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.sign_up_upper),
color = Color.White,
backgroundColor = Color(0xffda3832)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
ActionButton(
modifier = Modifier
.width(162.dp)
.height(48.dp),
text = stringResource(R.string.login_upper),
backgroundImage = R.mipmap.rider_pro_grey_bg_big
) {
navController.navigate(
NavigationRoute.EmailSignUp.route,
)
}
Spacer(modifier = Modifier.height(16.dp))
ActionButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.sign_in_with_google),
color = Color.Black,
leading = {
Image(
painter = painterResource(id = R.drawable.rider_pro_google),
contentDescription = "Google",
modifier = Modifier.size(36.dp)
)
},
expandText = true,
contentPadding = PaddingValues(vertical = 8.dp, horizontal = 8.dp)
) {
googleLogin()
}
Spacer(modifier = Modifier.height(32.dp))
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth()
) {
Text(
stringResource(R.string.login_upper),
fontSize = 16.sp,
fontWeight = FontWeight.W600,
color = Color(0xff333333),
modifier = Modifier.noRippleClickable {
navController.navigate(
NavigationRoute.UserAuth.route,
)
}
ActionButton(
modifier = Modifier
.width(162.dp)
.height(48.dp),
text = stringResource(R.string.sign_in_upper),
backgroundImage = R.mipmap.rider_pro_red_bg_big
){
navController.navigate(
NavigationRoute.SignUp.route,
)
}
}
)
}
Spacer(modifier = Modifier.height(120.dp))
}
}
}

View File

@@ -8,6 +8,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -164,7 +165,6 @@ fun UserAuthScreen() {
onValueChange = {
email = it
},
label = stringResource(R.string.login_email_label),
hint = stringResource(R.string.text_hint_email),
error = emailError
)
@@ -177,7 +177,6 @@ fun UserAuthScreen() {
password = it
},
password = true,
label = stringResource(R.string.login_password_label),
hint = stringResource(R.string.text_hint_password),
error = passwordError
)
@@ -229,34 +228,31 @@ fun UserAuthScreen() {
}
Spacer(modifier = Modifier.height(64.dp))
ActionButton(
modifier = Modifier
.width(345.dp)
.height(48.dp),
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.lets_ride_upper),
backgroundImage = R.mipmap.rider_pro_signup_red_bg
backgroundColor = Color(0xffda3832),
color = Color.White
) {
onLogin()
}
Spacer(modifier = Modifier.height(48.dp))
Text(stringResource(R.string.or_login_with), color = Color(0xFF999999))
Spacer(modifier = Modifier.height(16.dp))
Row {
Box(
modifier = Modifier
.size(96.dp)
.padding(16.dp)
.border(2.dp, Color(0xFFEBEBEB))
.noRippleClickable {
// login with facebook
googleLogin()
}
) {
ActionButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.sign_in_with_google),
color = Color.Black,
leading = {
Image(
painter = painterResource(id = R.drawable.rider_pro_google),
contentDescription = "Google",
modifier = Modifier.fillMaxSize()
modifier = Modifier.size(36.dp)
)
}
},
expandText = true,
contentPadding = PaddingValues(vertical = 8.dp, horizontal = 8.dp)
){
googleLogin()
}
}

View File

@@ -29,7 +29,7 @@
<string name="text_error_password_required">密码是必填项</string>
<string name="text_hint_email">输入邮箱</string>
<string name="text_hint_password">输入密码</string>
<string name="sign_in_upper">注册</string>
<string name="sign_up_upper">注册</string>
<string name="sign_in_with_email">使用邮箱注册</string>
<string name="sign_in_with_google">使用 Google 账号登录</string>
<string name="back_upper">返回</string>

View File

@@ -28,7 +28,7 @@
<string name="text_error_password_required">Password is required</string>
<string name="text_hint_email">Enter your email</string>
<string name="text_hint_password">Enter your password</string>
<string name="sign_in_upper">SIGN IN</string>
<string name="sign_up_upper">SIGN UP</string>
<string name="sign_in_with_email">CONTINUE WITH EMAIL</string>
<string name="sign_in_with_google">CONTINUE WITH GOOGLE</string>
<string name="back_upper">BACK</string>