新增重置密码校验

This commit is contained in:
2024-10-04 23:37:38 +08:00
parent c2764754fd
commit 40bbb8a0a0
11 changed files with 199 additions and 100 deletions

View File

@@ -4,8 +4,9 @@ import android.content.Context
object ConstVars {
// api 地址
// const val BASE_SERVER = "http://192.168.31.190:8088" const val BASE_SERVER = "https://8.137.22.101:8088"
const val BASE_SERVER = "https://8.137.22.101:8088"
const val BASE_SERVER = "http://192.168.31.130:8088"
// const val BASE_SERVER = "https://8.137.22.101: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"
@@ -27,10 +28,15 @@ object ConstVars {
*/
const val BANNER_IMAGE_MAX_SIZE = 2048
// 用户协议地址
const val DICT_KEY_PRIVATE_POLICY_URL = "private_policy"
// 重置邮箱间隔
const val DIC_KEY_RESET_EMAIL_INTERVAL = "send_reset_password_timeout"
}
//
enum class ErrorCode(val code: Int) {
USER_EXIST(10001)
USER_EXIST(40001),
USER_NOT_EXIST(40002),
}
fun Context.getErrorMessageCode(code: Int?): String {

View File

@@ -3,6 +3,7 @@ package com.aiosman.riderpro.data
import com.aiosman.riderpro.data.api.ApiClient
import com.aiosman.riderpro.data.api.DictItem
interface DictService {
/**
* 获取字典项

View File

@@ -1,19 +1,17 @@
package com.aiosman.riderpro.ui.account
import android.widget.Toast
import androidx.compose.foundation.Image
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.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.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -23,33 +21,41 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.ConstVars
import com.aiosman.riderpro.ErrorCode
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.DictService
import com.aiosman.riderpro.data.DictServiceImpl
import com.aiosman.riderpro.data.ServiceException
import com.aiosman.riderpro.getErrorMessageCode
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 com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun ResetPasswordScreen() {
var username by remember { mutableStateOf("") }
val accountService: AccountService = AccountServiceImpl()
val dictService: DictService = DictServiceImpl()
val scope = rememberCoroutineScope()
val context = LocalContext.current
var isSendSuccess by remember { mutableStateOf<Boolean?>(null) }
var isLoading by remember { mutableStateOf(false) }
val navController = LocalNavController.current
var usernameError by remember { mutableStateOf<String?>(null) }
var countDown by remember { mutableStateOf<Int?>(null) }
var countDownMax by remember { mutableStateOf(60) }
fun validate(): Boolean {
if (username.isEmpty()) {
usernameError = context.getString(R.string.text_error_email_required)
@@ -58,6 +64,28 @@ fun ResetPasswordScreen() {
usernameError = null
return true
}
LaunchedEffect(Unit) {
try {
dictService.getDictByKey(ConstVars.DIC_KEY_RESET_EMAIL_INTERVAL).let {
countDownMax = it.value.toInt()
}
} catch (e: Exception) {
countDownMax = 60
}
}
fun startCountDown() {
scope.launch {
countDown = countDownMax
while (countDown!! > 0) {
delay(1000)
countDown = countDown!! - 1
}
countDown = null
}
}
fun resetPassword() {
if (!validate()) return
scope.launch {
@@ -65,6 +93,13 @@ fun ResetPasswordScreen() {
try {
accountService.resetPassword(username)
isSendSuccess = true
startCountDown()
} catch (e: ServiceException) {
if (e.code == ErrorCode.USER_NOT_EXIST.code){
usernameError = context.getString(R.string.error_40002_user_not_exist)
} else {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
isSendSuccess = false
@@ -74,6 +109,7 @@ fun ResetPasswordScreen() {
}
}
Column(
modifier = Modifier.fillMaxSize()
) {
@@ -91,80 +127,78 @@ fun ResetPasswordScreen() {
moreIcon = false
)
}
Spacer(modifier = Modifier.height(72.dp))
Column(
modifier = Modifier.padding(horizontal = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(36.dp))
if (isSendSuccess != null) {
if (isSendSuccess!!) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.reset_mail_send_success),
style = TextStyle(
color = Color(0xFF333333),
fontSize = 14.sp,
fontWeight = FontWeight.Bold
TextInputField(
text = username,
onValueChange = { username = it },
hint = stringResource(R.string.text_hint_email),
enabled = !isLoading && countDown == null,
error = usernameError,
)
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier.height(72.dp)
) {
isSendSuccess?.let {
if (it) {
Text(
text = stringResource(R.string.reset_mail_send_success),
style = TextStyle(
color = Color(0xFF333333),
fontSize = 14.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier.fillMaxSize()
)
)
} else {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.reset_mail_send_failed),
style = TextStyle(
color = Color(0xFF333333),
fontSize = 14.sp,
fontWeight = FontWeight.Bold
} else {
Text(
text = stringResource(R.string.reset_mail_send_failed),
style = TextStyle(
color = Color(0xFF333333),
fontSize = 14.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier.fillMaxSize()
)
)
}
Spacer(modifier = Modifier.height(40.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.noRippleClickable {
navController.popBackStack()
}
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_nav_back),
contentDescription = "Back",
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(8.dp))
androidx.compose.material3.Text(
stringResource(R.string.back_upper),
color = Color.Black,
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
}
} else {
Spacer(modifier = Modifier.height(120.dp))
TextInputField(
text = username,
onValueChange = { username = it },
hint = stringResource(R.string.text_hint_email),
enabled = !isLoading,
error = usernameError,
)
Spacer(modifier = Modifier.height(72.dp))
if (isLoading) {
CircularProgressIndicator()
} else {
ActionButton(
modifier = Modifier
.width(345.dp)
.height(48.dp),
text = stringResource(R.string.recover),
backgroundColor = Color(0xffda3832),
) {
resetPassword()
}
}
}
ActionButton(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
text = if (countDown != null) {
stringResource(R.string.resend, "(${countDown})")
} else {
stringResource(R.string.recover)
},
backgroundColor = Color(0xffda3832),
color = Color.White,
isLoading = isLoading,
contentPadding = PaddingValues(0.dp),
enabled = countDown == null,
) {
resetPassword()
}
isSendSuccess?.let {
Spacer(modifier = Modifier.height(16.dp))
ActionButton(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
text = stringResource(R.string.back_upper),
contentPadding = PaddingValues(0.dp),
) {
navController.popBackStack()
}
}
}
}
}

View File

@@ -1,14 +1,21 @@
package com.aiosman.riderpro.ui.composables
import androidx.annotation.DrawableRes
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
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.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -29,33 +36,82 @@ fun ActionButton(
leading: @Composable (() -> Unit)? = null,
expandText: Boolean = false,
contentPadding: PaddingValues = PaddingValues(vertical = 16.dp),
isLoading: Boolean = false,
loadingTextColor: Color = Color.White,
loadingText: String = "Loading",
loadingBackgroundColor: Color = Color(0xFFD95757),
disabledBackgroundColor: Color = Color(0xFFD0D0D0),
enabled: Boolean = true,
click: () -> Unit = {}
) {
val animatedBackgroundColor by animateColorAsState(
targetValue = run{
if (enabled) {
if (isLoading) {
loadingBackgroundColor
} else {
backgroundColor
}
} else {
disabledBackgroundColor
}
},
animationSpec = tween(300)
)
Box(
modifier = modifier
.clip(RoundedCornerShape(24.dp))
.background(backgroundColor)
.background(animatedBackgroundColor)
.padding(contentPadding)
.noRippleClickable {
click()
}
if (enabled && !isLoading) {
click()
}
},
contentAlignment = Alignment.CenterStart
) {
Row(
modifier = Modifier.align(Alignment.Center),
verticalAlignment = Alignment.CenterVertically
) {
leading?.invoke()
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
)
if (!isLoading) {
Row(
modifier = Modifier.align(Alignment.Center),
verticalAlignment = Alignment.CenterVertically
) {
leading?.invoke()
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
)
}
}else{
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.CenterStart
) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = Color.White
)
Text(
loadingText,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
color = loadingTextColor
)
}
}
}
}
}

View File

@@ -28,7 +28,7 @@ fun Checkbox(
) {
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)
val borderWidth by animateDpAsState(if (checked) 0.dp else 2.dp)
Box(
modifier = Modifier

View File

@@ -31,6 +31,7 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import com.aiosman.riderpro.ConstVars
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.DictService
import com.aiosman.riderpro.data.DictServiceImpl
@@ -53,7 +54,7 @@ fun PolicyCheckbox(
fun openPolicyModel() {
scope.launch {
try {
val resp = dictService.getDictByKey("private_policy")
val resp = dictService.getDictByKey(ConstVars.DICT_KEY_PRIVATE_POLICY_URL)
policyUrl = resp.value
showModal = true

View File

@@ -67,7 +67,7 @@ fun TextInputField(
.clip(RoundedCornerShape(24.dp))
.background(Color(0xFFF7f7f7))
.border(
width = 1.dp,
width = 2.dp,
color = if (error == null) Color.Transparent else Color(0xFFE53935),
shape = RoundedCornerShape(24.dp)
)

View File

@@ -1,12 +1,9 @@
package com.aiosman.riderpro.ui.index.tabs.profile
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background

View File

@@ -168,7 +168,7 @@ fun UserAuthScreen() {
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(),

View File

@@ -79,4 +79,6 @@
<string name="like_your_post">喜欢了你的动态</string>
<string name="favourite_your_post">收藏了你的动态</string>
<string name="like_your_comment">喜欢了你的评论</string>
<string name="resend">重新发送 %s</string>
<string name="error_40002_user_not_exist">用户不存在</string>
</resources>

View File

@@ -78,4 +78,6 @@
<string name="like_your_post">Like your post</string>
<string name="favourite_your_post">Favourite your post</string>
<string name="like_your_comment">Like your comment</string>
<string name="resend">Resend in %s</string>
<string name="error_40002_user_not_exist">user not exist</string>
</resources>