新增重置密码校验
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
* 获取字典项
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user