diff --git a/app/src/main/java/com/aiosman/riderpro/Const.kt b/app/src/main/java/com/aiosman/riderpro/Const.kt index 9e1edf3..cb05eb2 100644 --- a/app/src/main/java/com/aiosman/riderpro/Const.kt +++ b/app/src/main/java/com/aiosman/riderpro/Const.kt @@ -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 { diff --git a/app/src/main/java/com/aiosman/riderpro/data/DictService.kt b/app/src/main/java/com/aiosman/riderpro/data/DictService.kt index 6663ca6..9c66743 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/DictService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/DictService.kt @@ -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 { /** * 获取字典项 diff --git a/app/src/main/java/com/aiosman/riderpro/ui/account/ResetPassword.kt b/app/src/main/java/com/aiosman/riderpro/ui/account/ResetPassword.kt index cf638c2..bde00e9 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/account/ResetPassword.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/account/ResetPassword.kt @@ -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(null) } var isLoading by remember { mutableStateOf(false) } val navController = LocalNavController.current var usernameError by remember { mutableStateOf(null) } + var countDown by remember { mutableStateOf(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() + } + + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/composables/ActionButton.kt b/app/src/main/java/com/aiosman/riderpro/ui/composables/ActionButton.kt index eb103a7..5c79eff 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/composables/ActionButton.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/composables/ActionButton.kt @@ -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 + ) + } + } } + } } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/composables/Checkbox.kt b/app/src/main/java/com/aiosman/riderpro/ui/composables/Checkbox.kt index 4573c52..a5ab822 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/composables/Checkbox.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/composables/Checkbox.kt @@ -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 diff --git a/app/src/main/java/com/aiosman/riderpro/ui/composables/PolicyCheckbox.kt b/app/src/main/java/com/aiosman/riderpro/ui/composables/PolicyCheckbox.kt index cdebfbd..6b4e211 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/composables/PolicyCheckbox.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/composables/PolicyCheckbox.kt @@ -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 diff --git a/app/src/main/java/com/aiosman/riderpro/ui/composables/TextInputField.kt b/app/src/main/java/com/aiosman/riderpro/ui/composables/TextInputField.kt index 875ed21..f4b64be 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/composables/TextInputField.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/composables/TextInputField.kt @@ -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) ) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profilev2.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profilev2.kt index 6070108..37add70 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profilev2.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profilev2.kt @@ -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 diff --git a/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt b/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt index a1be3c1..246ce80 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt @@ -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(), diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index b70f712..6f569c8 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -79,4 +79,6 @@ 喜欢了你的动态 收藏了你的动态 喜欢了你的评论 + 重新发送 %s + 用户不存在 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 56b3f40..0a98c01 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,4 +78,6 @@ Like your post Favourite your post Like your comment + Resend in %s + user not exist \ No newline at end of file