新增邮箱注册页面表单校验
新增邮箱注册页面表单校验,包括: - 邮箱格式校验 - 密码格式校验(至少8位, 包含大小写字母和数字) - 确认密码校验 - 服务条款和推广信息勾选校验 新增错误提示,包括: - 邮箱格式错误 - 密码格式错误 - 确认密码不一致 - 未勾选服务条款 - 未勾选推广信息 优化 用户体验,包括: - 使用 CheckboxWithLabel 组件优化勾选框样式 - 使用字符串资源 - 调整页面布局
This commit is contained in:
@@ -1,11 +1,24 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.content.Context
|
||||
|
||||
object ConstVars {
|
||||
// api 地址
|
||||
// const val BASE_SERVER = "http://192.168.31.190:8088"
|
||||
// const val BASE_SERVER = "http://192.168.31.36:8088"
|
||||
const val BASE_SERVER = "https://8.137.22.101:8088"
|
||||
const val BASE_SERVER = "http://192.168.31.36:8088"
|
||||
|
||||
// const val BASE_SERVER = "https://8.137.22.101:8088"
|
||||
const val MOMENT_LIKE_CHANNEL_ID = "moment_like"
|
||||
const val MOMENT_LIKE_CHANNEL_NAME = "Moment Like"
|
||||
|
||||
}
|
||||
|
||||
enum class ErrorCode(val code: Int) {
|
||||
USER_EXIST(10001)
|
||||
}
|
||||
fun Context.getErrorMessageCode(code: Int?): String {
|
||||
return when (code) {
|
||||
10001 -> getString(R.string.error_10001_user_exist)
|
||||
else -> getString(R.string.error_unknown)
|
||||
}
|
||||
}
|
||||
@@ -365,7 +365,14 @@ class AccountServiceImpl : AccountService {
|
||||
}
|
||||
|
||||
override suspend fun registerUserWithPassword(loginName: String, password: String) {
|
||||
ApiClient.api.register(RegisterRequestBody(loginName, password))
|
||||
val resp = ApiClient.api.register(RegisterRequestBody(loginName, password))
|
||||
|
||||
if (!resp.isSuccessful) {
|
||||
parseErrorResponse(resp.errorBody())?.let {
|
||||
throw it.toServiceException()
|
||||
}
|
||||
throw ServiceException("Failed to register")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun changeAccountPassword(oldPassword: String, newPassword: String) {
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
package com.aiosman.riderpro.data
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
/**
|
||||
* 错误返回
|
||||
*/
|
||||
class ServiceException(
|
||||
override val message: String,
|
||||
val code: Int = 0,
|
||||
val data: Any? = null
|
||||
val code: Int? = 0,
|
||||
val data: Any? = null,
|
||||
val error: String? = null,
|
||||
val name: String? = null,
|
||||
) : Exception(
|
||||
message
|
||||
)
|
||||
|
||||
data class ApiErrorResponse(
|
||||
@SerializedName("code")
|
||||
val code: Int?,
|
||||
@SerializedName("error")
|
||||
val error: String?,
|
||||
@SerializedName("message")
|
||||
val name: String?,
|
||||
) {
|
||||
fun toServiceException(): ServiceException {
|
||||
return ServiceException(
|
||||
message = error ?: name ?: "",
|
||||
code = code,
|
||||
error = error,
|
||||
name = name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseErrorResponse(errorBody: ResponseBody?): ApiErrorResponse? {
|
||||
return errorBody?.let {
|
||||
val gson = Gson()
|
||||
gson.fromJson(it.charStream(), ApiErrorResponse::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.aiosman.riderpro.ui.composables
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
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.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.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
|
||||
|
||||
@Composable
|
||||
fun Checkbox(
|
||||
size: Int = 24,
|
||||
checked: Boolean = false,
|
||||
onCheckedChange: (Boolean) -> Unit = {}
|
||||
) {
|
||||
val backgroundColor by animateColorAsState(if (checked) Color.Black else Color.Transparent)
|
||||
val borderColor by animateColorAsState(if (checked) Color.Transparent else Color(0xffebebeb))
|
||||
val borderWidth by animateDpAsState(if (checked) 0.dp else 1.dp)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(size.dp)
|
||||
.noRippleClickable {
|
||||
onCheckedChange(!checked)
|
||||
}
|
||||
.background(color = backgroundColor)
|
||||
.border(width = borderWidth, color = borderColor)
|
||||
.padding(2.dp)
|
||||
) {
|
||||
if (checked) {
|
||||
Icon(
|
||||
Icons.Default.Check,
|
||||
contentDescription = "Checked",
|
||||
tint = Color.White,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.aiosman.riderpro.ui.composables
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun CheckboxWithLabel(
|
||||
checked: Boolean = false,
|
||||
checkSize: Int = 16,
|
||||
label: String = "",
|
||||
fontSize: Int = 12,
|
||||
error: Boolean = false,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = {
|
||||
onCheckedChange(it)
|
||||
},
|
||||
size = checkSize
|
||||
)
|
||||
Text(
|
||||
text = label,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
fontSize = fontSize.sp,
|
||||
style = TextStyle(
|
||||
color = if (error) Color.Red else Color.Black
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
package com.aiosman.riderpro.ui.login
|
||||
|
||||
import android.widget.Toast
|
||||
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.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.layout.width
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -20,30 +24,37 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
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
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.getErrorMessageCode
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||
import com.aiosman.riderpro.ui.composables.Checkbox
|
||||
import com.aiosman.riderpro.ui.composables.CheckboxWithLabel
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.riderpro.ui.composables.TextInputField
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun EmailSignupScreen() {
|
||||
var email by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var confirmPassword by remember { mutableStateOf("") }
|
||||
var email by remember { mutableStateOf("takayamaaren@gmail.com") }
|
||||
var password by remember { mutableStateOf("Dzh17217.") }
|
||||
var confirmPassword by remember { mutableStateOf("Dzh17217.") }
|
||||
var rememberMe by remember { mutableStateOf(false) }
|
||||
var acceptTerms by remember { mutableStateOf(false) }
|
||||
var acceptPromotions by remember { mutableStateOf(false) }
|
||||
@@ -51,32 +62,62 @@ fun EmailSignupScreen() {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
var emailError by remember { mutableStateOf<String?>(null) }
|
||||
var passwordError by remember { mutableStateOf<String?>(null) }
|
||||
var confirmPasswordError by remember { mutableStateOf<String?>(null) }
|
||||
var termsError by remember { mutableStateOf<Boolean>(false) }
|
||||
var promotionsError by remember { mutableStateOf<Boolean>(false) }
|
||||
|
||||
fun validateForm(): Boolean {
|
||||
if (email.isEmpty()) {
|
||||
Toast.makeText(context, "Email is required", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
emailError = when {
|
||||
// 非空
|
||||
email.isEmpty() -> context.getString(R.string.text_error_email_required)
|
||||
// 邮箱格式
|
||||
!android.util.Patterns.EMAIL_ADDRESS.matcher(email)
|
||||
.matches() -> context.getString(R.string.text_error_email_format)
|
||||
|
||||
else -> null
|
||||
}
|
||||
if (password.isEmpty()) {
|
||||
Toast.makeText(context, "Password is required", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
passwordError = when {
|
||||
// 非空
|
||||
password.isEmpty() -> context.getString(R.string.text_error_password_required)
|
||||
// 包含大写字母
|
||||
!password.matches(Regex(".*[A-Z].*")) -> context.getString(R.string.text_error_password_format)
|
||||
// 至少8位
|
||||
password.length < 8 -> context.getString(R.string.text_error_password_format)
|
||||
// 至少一个数字
|
||||
!password.matches(Regex(".*\\d.*")) -> context.getString(R.string.text_error_password_format)
|
||||
// 包含小写字母
|
||||
!password.matches(Regex(".*[a-z].*")) -> context.getString(R.string.text_error_password_format)
|
||||
else -> null
|
||||
}
|
||||
if (confirmPassword.isEmpty()) {
|
||||
Toast.makeText(context, "Confirm password is required", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
}
|
||||
if (password != confirmPassword) {
|
||||
Toast.makeText(context, "Password does not match", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
confirmPasswordError = when {
|
||||
// 非空
|
||||
confirmPassword.isEmpty() -> context.getString(R.string.text_error_confirm_password_required)
|
||||
// 与密码一致
|
||||
confirmPassword != password -> context.getString(R.string.text_error_confirm_password_mismatch)
|
||||
else -> null
|
||||
}
|
||||
if (!acceptTerms) {
|
||||
Toast.makeText(context, "You must accept terms", Toast.LENGTH_SHORT).show()
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, context.getString(R.string.error_not_accept_term), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
termsError = true
|
||||
return false
|
||||
}else{
|
||||
termsError = false
|
||||
}
|
||||
if (!acceptPromotions) {
|
||||
Toast.makeText(context, "You must accept promotions", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, context.getString(R.string.error_not_accept_recive_notice), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return true
|
||||
promotionsError = true
|
||||
return false
|
||||
}else{
|
||||
promotionsError = false
|
||||
}
|
||||
|
||||
return emailError == null && passwordError == null && confirmPasswordError == null
|
||||
}
|
||||
|
||||
suspend fun registerUser() {
|
||||
@@ -86,8 +127,18 @@ fun EmailSignupScreen() {
|
||||
accountService.registerUserWithPassword(email, password)
|
||||
} catch (e: ServiceException) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Failed to register", Toast.LENGTH_SHORT).show()
|
||||
if (e.code == ErrorCode.USER_EXIST.code) {
|
||||
emailError = context.getString(R.string.error_10001_user_exist)
|
||||
return@launch
|
||||
}
|
||||
Toast.makeText(context, context.getErrorMessageCode(e.code), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return
|
||||
}
|
||||
// 获取 token
|
||||
val authResp = accountService.loginUserWithPassword(email, password)
|
||||
@@ -115,118 +166,98 @@ fun EmailSignupScreen() {
|
||||
popUpTo(NavigationRoute.Login.route) { inclusive = true }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
StatusBarMaskLayout {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.White)
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
|
||||
) {
|
||||
NoticeScreenHeader("SIGNUP", moreIcon = false)
|
||||
NoticeScreenHeader(stringResource(R.string.sign_in_upper), moreIcon = false)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(68.dp))
|
||||
TextInputField(
|
||||
Spacer(modifier = Modifier.padding(32.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp),
|
||||
.weight(1f)
|
||||
.padding(horizontal = 24.dp)
|
||||
) {
|
||||
TextInputField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
text = email,
|
||||
onValueChange = {
|
||||
email = it
|
||||
},
|
||||
label = "What's your email?",
|
||||
hint = "Enter your email"
|
||||
label = stringResource(R.string.login_email_label),
|
||||
hint = stringResource(R.string.text_hint_email),
|
||||
error = emailError
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
TextInputField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp),
|
||||
.fillMaxWidth(),
|
||||
text = password,
|
||||
onValueChange = {
|
||||
password = it
|
||||
},
|
||||
password = true,
|
||||
label = "What's your password?",
|
||||
hint = "Enter your password"
|
||||
label = stringResource(R.string.login_password_label),
|
||||
hint = stringResource(R.string.text_hint_password),
|
||||
error = passwordError
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
TextInputField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp),
|
||||
.fillMaxWidth(),
|
||||
text = confirmPassword,
|
||||
onValueChange = {
|
||||
confirmPassword = it
|
||||
},
|
||||
password = true,
|
||||
label = "Confirm password",
|
||||
hint = "Enter your password"
|
||||
label = stringResource(R.string.login_confirm_password_label),
|
||||
hint = stringResource(R.string.text_hint_confirm_password),
|
||||
error = confirmPasswordError
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Checkbox(
|
||||
CheckboxWithLabel(
|
||||
checked = rememberMe,
|
||||
onCheckedChange = {
|
||||
checkSize = 16,
|
||||
fontSize = 12,
|
||||
label = stringResource(R.string.remember_me),
|
||||
) {
|
||||
rememberMe = it
|
||||
},
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = Color.Black
|
||||
),
|
||||
)
|
||||
Text("Remember me", modifier = Modifier.padding(start = 4.dp))
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Checkbox(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
CheckboxWithLabel(
|
||||
checked = acceptTerms,
|
||||
onCheckedChange = {
|
||||
acceptTerms = it
|
||||
},
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = Color.Black
|
||||
),
|
||||
)
|
||||
Text(
|
||||
"Yes, I have read and agree to RiderPro’s Terms of Service.",
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
checkSize = 16,
|
||||
fontSize = 12,
|
||||
label = stringResource(R.string.agree_terms_of_service),
|
||||
error = termsError
|
||||
) {
|
||||
Checkbox(
|
||||
acceptTerms = it
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
CheckboxWithLabel(
|
||||
checked = acceptPromotions,
|
||||
onCheckedChange = {
|
||||
checkSize = 16,
|
||||
fontSize = 12,
|
||||
label = stringResource(R.string.agree_promotion),
|
||||
error = promotionsError
|
||||
) {
|
||||
acceptPromotions = it
|
||||
},
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = Color.Black
|
||||
),
|
||||
)
|
||||
Text(
|
||||
"Yes, Send me news and promotional content from RiderPro.",
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +266,7 @@ fun EmailSignupScreen() {
|
||||
modifier = Modifier
|
||||
.width(345.dp)
|
||||
.height(48.dp),
|
||||
text = "LET'S RIDE".uppercase(),
|
||||
text = stringResource(R.string.lets_ride_upper),
|
||||
backgroundImage = R.mipmap.rider_pro_signup_red_bg
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
@@ -243,5 +274,7 @@ fun EmailSignupScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,4 +33,16 @@
|
||||
<string name="sign_in_with_email">使用邮箱注册</string>
|
||||
<string name="sign_in_with_google">使用 Google 账号登录</string>
|
||||
<string name="back_upper">返回</string>
|
||||
<string name="text_hint_confirm_password">再次输入密码</string>
|
||||
<string name="login_confirm_password_label">再次输入密码</string>
|
||||
<string name="agree_terms_of_service">我已阅读用户协议</string>
|
||||
<string name="agree_promotion">我同意 Rider Pro 推送消息</string>
|
||||
<string name="text_error_email_format">邮箱格式错误</string>
|
||||
<string name="text_error_password_format">密码至少为 8 位,包含大写字母、小写字母、数字</string>
|
||||
<string name="text_error_confirm_password_mismatch">密码和确认密码必须相同</string>
|
||||
<string name="text_error_confirm_password_required">请输入确认密码</string>
|
||||
<string name="error_10001_user_exist">用户已存在</string>
|
||||
<string name="error_unknown">服务端未知错误</string>
|
||||
<string name="error_not_accept_recive_notice">为了为您提供更个性化的服务,请允许我们向您推送相关信息。</string>
|
||||
<string name="error_not_accept_term">"为了提供更好的服务,请您在注册前仔细阅读并同意《用户协议》。 "</string>
|
||||
</resources>
|
||||
@@ -32,4 +32,16 @@
|
||||
<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>
|
||||
<string name="text_hint_confirm_password">Enter your password again</string>
|
||||
<string name="login_confirm_password_label">Confirm password</string>
|
||||
<string name="agree_terms_of_service">Yes, I have read and agree to RiderPro’s Terms of Service.</string>
|
||||
<string name="agree_promotion">Yes, I would like to receive promotions and updates from RiderPro.</string>
|
||||
<string name="text_error_email_format">Invalid email</string>
|
||||
<string name="text_error_password_format">The password must be at least 8 characters long and contain a combination of uppercase letters, lowercase letters, and numbers.</string>
|
||||
<string name="text_error_confirm_password_mismatch">Please ensure that the passwords entered twice are consistent.</string>
|
||||
<string name="text_error_confirm_password_required">Confirm password is required</string>
|
||||
<string name="error_10001_user_exist">User existed</string>
|
||||
<string name="error_unknown">Unknown error</string>
|
||||
<string name="error_not_accept_recive_notice">To provide you with a more personalized experience, please allow us to send you relevant notifications.</string>
|
||||
<string name="error_not_accept_term">To provide you with the best service, please read and agree to our User Agreement before registering.</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user