调整细节
This commit is contained in:
@@ -460,11 +460,17 @@ class AccountServiceImpl : AccountService {
|
||||
}
|
||||
|
||||
override suspend fun resetPassword(email: String) {
|
||||
ApiClient.api.resetPassword(
|
||||
val resp = ApiClient.api.resetPassword(
|
||||
ResetPasswordRequestBody(
|
||||
username = email
|
||||
)
|
||||
)
|
||||
if (!resp.isSuccessful) {
|
||||
parseErrorResponse(resp.errorBody())?.let {
|
||||
throw it.toServiceException()
|
||||
}
|
||||
throw ServiceException("Failed to reset password")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,8 +49,17 @@ fun ResetPasswordScreen() {
|
||||
var isSendSuccess by remember { mutableStateOf<Boolean?>(null) }
|
||||
var isLoading by remember { mutableStateOf(false) }
|
||||
val navController = LocalNavController.current
|
||||
|
||||
var usernameError by remember { mutableStateOf<String?>(null) }
|
||||
fun validate(): Boolean {
|
||||
if (username.isEmpty()) {
|
||||
usernameError = context.getString(R.string.text_error_email_required)
|
||||
return false
|
||||
}
|
||||
usernameError = null
|
||||
return true
|
||||
}
|
||||
fun resetPassword() {
|
||||
if (!validate()) return
|
||||
scope.launch {
|
||||
isLoading = true
|
||||
try {
|
||||
@@ -78,7 +87,7 @@ fun ResetPasswordScreen() {
|
||||
)
|
||||
) {
|
||||
NoticeScreenHeader(
|
||||
"RECOVER ACCOUNT",
|
||||
stringResource(R.string.recover_account_upper),
|
||||
moreIcon = false
|
||||
)
|
||||
}
|
||||
@@ -93,7 +102,7 @@ fun ResetPasswordScreen() {
|
||||
if (isSendSuccess!!) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Reset password email has been sent to your email address",
|
||||
text = stringResource(R.string.reset_mail_send_success),
|
||||
style = TextStyle(
|
||||
color = Color(0xFF333333),
|
||||
fontSize = 14.sp,
|
||||
@@ -103,7 +112,7 @@ fun ResetPasswordScreen() {
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Failed to send reset password email",
|
||||
text = stringResource(R.string.reset_mail_send_failed),
|
||||
style = TextStyle(
|
||||
color = Color(0xFF333333),
|
||||
fontSize = 14.sp,
|
||||
@@ -138,7 +147,8 @@ fun ResetPasswordScreen() {
|
||||
onValueChange = { username = it },
|
||||
label = stringResource(R.string.login_email_label),
|
||||
hint = stringResource(R.string.text_hint_email),
|
||||
enabled = !isLoading
|
||||
enabled = !isLoading,
|
||||
error = usernameError
|
||||
)
|
||||
Spacer(modifier = Modifier.height(72.dp))
|
||||
if (isLoading) {
|
||||
@@ -148,7 +158,7 @@ fun ResetPasswordScreen() {
|
||||
modifier = Modifier
|
||||
.width(345.dp)
|
||||
.height(48.dp),
|
||||
text = "Recover Account",
|
||||
text = stringResource(R.string.recover),
|
||||
backgroundImage = R.mipmap.rider_pro_signup_red_bg
|
||||
) {
|
||||
resetPassword()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.aiosman.riderpro.ui.composables
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
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.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
|
||||
@Composable
|
||||
fun FollowButton(
|
||||
isFollowing: Boolean,
|
||||
fontSize: TextUnit = 12.sp,
|
||||
imageModifier: Modifier = Modifier,
|
||||
onFollowClick: () -> Unit,
|
||||
){
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.padding(start = 6.dp)
|
||||
.noRippleClickable {
|
||||
onFollowClick()
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
modifier = imageModifier,
|
||||
painter = painterResource(id = R.drawable.follow_bg),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.FillWidth
|
||||
)
|
||||
Text(
|
||||
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(
|
||||
R.string.follow_upper
|
||||
),
|
||||
fontSize = fontSize,
|
||||
color = Color.White,
|
||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.aiosman.riderpro.ui.composables
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -112,16 +116,23 @@ fun TextInputField(
|
||||
.height(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (error != null) {
|
||||
AnimatedVisibility(
|
||||
visible = error != null,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.rider_pro_input_error),
|
||||
contentDescription = "Error",
|
||||
modifier = Modifier.size(8.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(4.dp))
|
||||
Text(error, color = Color(0xFFE53935), fontSize = 12.sp)
|
||||
AnimatedContent(targetState = error) { targetError ->
|
||||
Text(targetError ?: "", color = Color(0xFFE53935), fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ fun FavouriteNoticeScreen() {
|
||||
var dataFlow = model.favouriteItemsFlow
|
||||
var favourites = dataFlow.collectAsLazyPagingItems()
|
||||
LaunchedEffect(Unit) {
|
||||
model.reload()
|
||||
model.updateNotice()
|
||||
}
|
||||
StatusBarMaskLayout(
|
||||
|
||||
@@ -26,8 +26,13 @@ object FavouriteNoticeViewModel : ViewModel() {
|
||||
private val _favouriteItemsFlow =
|
||||
MutableStateFlow<PagingData<AccountFavouriteEntity>>(PagingData.empty())
|
||||
val favouriteItemsFlow = _favouriteItemsFlow.asStateFlow()
|
||||
var isFirstLoad = true
|
||||
|
||||
init {
|
||||
fun reload(force: Boolean = false) {
|
||||
if (!isFirstLoad && !force) {
|
||||
return
|
||||
}
|
||||
isFirstLoad = false
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
|
||||
@@ -47,6 +47,9 @@ fun FollowerListScreen(userId: Int) {
|
||||
isFollowing = user.isFollowing
|
||||
) {
|
||||
scope.launch {
|
||||
if (user.isFollowing) {
|
||||
model.unFollowUser(user.id)
|
||||
} else {
|
||||
model.followUser(user.id)
|
||||
}
|
||||
}
|
||||
@@ -54,4 +57,5 @@ fun FollowerListScreen(userId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,11 +43,11 @@ object FollowerListViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateIsFollow(id: Int) {
|
||||
private fun updateIsFollow(id: Int, isFollow: Boolean = true) {
|
||||
val currentPagingData = usersFlow.value
|
||||
val updatedPagingData = currentPagingData.map { user ->
|
||||
if (user.id == id) {
|
||||
user.copy(isFollowing = true)
|
||||
user.copy(isFollowing = isFollow)
|
||||
} else {
|
||||
user
|
||||
}
|
||||
@@ -60,4 +60,9 @@ object FollowerListViewModel : ViewModel() {
|
||||
updateIsFollow(userId)
|
||||
}
|
||||
|
||||
suspend fun unFollowUser(userId: Int) {
|
||||
userService.unFollowUser(userId.toString())
|
||||
updateIsFollow(userId, false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,12 +25,14 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.AppState
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.FollowButton
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -47,11 +49,14 @@ fun FollowerNoticeScreen() {
|
||||
var dataFlow = model.followerItemsFlow
|
||||
var followers = dataFlow.collectAsLazyPagingItems()
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
NoticeScreenHeader(stringResource(R.string.followers_upper), moreIcon = false)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
model.reload()
|
||||
model.updateNotice()
|
||||
}
|
||||
LazyColumn(
|
||||
@@ -114,30 +119,43 @@ fun FollowItem(
|
||||
) {
|
||||
Text(nickname, fontWeight = FontWeight.Bold, fontSize = 16.sp)
|
||||
}
|
||||
if (!isFollowing) {
|
||||
Box(
|
||||
modifier = Modifier.noRippleClickable {
|
||||
onFollow()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.follow_bg),
|
||||
contentDescription = "Follow",
|
||||
modifier = Modifier
|
||||
.width(79.dp)
|
||||
if (userId != AppState.UserId) {
|
||||
FollowButton(
|
||||
isFollowing = isFollowing,
|
||||
onFollowClick = onFollow,
|
||||
fontSize = 14.sp,
|
||||
imageModifier = Modifier
|
||||
.width(100.dp)
|
||||
.height(24.dp)
|
||||
)
|
||||
Text(
|
||||
"FOLLOW",
|
||||
fontSize = 14.sp,
|
||||
color = Color(0xFFFFFFFF),
|
||||
modifier = Modifier.align(
|
||||
Alignment.Center
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Box(
|
||||
// modifier = Modifier.noRippleClickable {
|
||||
// onFollow()
|
||||
// }
|
||||
// ) {
|
||||
// Image(
|
||||
// painter = painterResource(id = R.drawable.follow_bg),
|
||||
// contentDescription = "Follow",
|
||||
// modifier = Modifier
|
||||
// .width(79.dp)
|
||||
// .height(24.dp)
|
||||
// )
|
||||
// Text(
|
||||
// text = if (isFollowing) {
|
||||
// stringResource(R.string.following_upper)
|
||||
// } else {
|
||||
// stringResource(R.string.follow_upper)
|
||||
// },
|
||||
// fontSize = 14.sp,
|
||||
// color = Color(0xFFFFFFFF),
|
||||
// modifier = Modifier.align(
|
||||
// Alignment.Center
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,13 @@ object FollowerNoticeViewModel : ViewModel() {
|
||||
private val _followerItemsFlow =
|
||||
MutableStateFlow<PagingData<AccountFollow>>(PagingData.empty())
|
||||
val followerItemsFlow = _followerItemsFlow.asStateFlow()
|
||||
var isFirstLoad = true
|
||||
|
||||
init {
|
||||
fun reload(force: Boolean = false) {
|
||||
if (!isFirstLoad && !force) {
|
||||
return
|
||||
}
|
||||
isFirstLoad = false
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
|
||||
@@ -18,7 +18,7 @@ import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun FollowingListScreen(userId: Int) {
|
||||
val model = FollowerListViewModel
|
||||
val model = FollowingListViewModel
|
||||
val scope = rememberCoroutineScope()
|
||||
LaunchedEffect(Unit) {
|
||||
model.loadData(userId)
|
||||
@@ -47,6 +47,9 @@ fun FollowingListScreen(userId: Int) {
|
||||
isFollowing = user.isFollowing
|
||||
) {
|
||||
scope.launch {
|
||||
if (user.isFollowing) {
|
||||
model.unfollowUser(user.id)
|
||||
} else {
|
||||
model.followUser(user.id)
|
||||
}
|
||||
}
|
||||
@@ -54,4 +57,5 @@ fun FollowingListScreen(userId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,6 @@ object FollowingListViewModel : ViewModel() {
|
||||
val usersFlow = _usersFlow.asStateFlow()
|
||||
private var userId by mutableStateOf<Int?>(null)
|
||||
fun loadData(id: Int) {
|
||||
if (userId == id) {
|
||||
return
|
||||
}
|
||||
userId = id
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
@@ -34,7 +31,7 @@ object FollowingListViewModel : ViewModel() {
|
||||
pagingSourceFactory = {
|
||||
AccountPagingSource(
|
||||
userService,
|
||||
followerId = id
|
||||
followingId = id
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
@@ -43,11 +40,11 @@ object FollowingListViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateIsFollow(id: Int) {
|
||||
private fun updateIsFollow(id: Int, isFollow: Boolean = true) {
|
||||
val currentPagingData = usersFlow.value
|
||||
val updatedPagingData = currentPagingData.map { user ->
|
||||
if (user.id == id) {
|
||||
user.copy(isFollowing = true)
|
||||
user.copy(isFollowing = isFollow)
|
||||
} else {
|
||||
user
|
||||
}
|
||||
@@ -60,4 +57,9 @@ object FollowingListViewModel : ViewModel() {
|
||||
updateIsFollow(userId)
|
||||
}
|
||||
|
||||
suspend fun unfollowUser(userId: Int) {
|
||||
userService.unFollowUser(userId.toString())
|
||||
updateIsFollow(userId, false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,6 +54,9 @@ import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.riderpro.ui.favourite.FavouriteNoticeViewModel
|
||||
import com.aiosman.riderpro.ui.follower.FollowerNoticeViewModel
|
||||
import com.aiosman.riderpro.ui.like.LikeNoticeViewModel
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.riderpro.ui.post.PostViewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
@@ -72,7 +75,7 @@ fun NotificationsScreen() {
|
||||
var comments = dataFlow.collectAsLazyPagingItems()
|
||||
val state = rememberPullRefreshState(MessageListViewModel.isLoading, onRefresh = {
|
||||
MessageListViewModel.viewModelScope.launch {
|
||||
MessageListViewModel.initData()
|
||||
MessageListViewModel.initData(force = true)
|
||||
}
|
||||
})
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -103,6 +106,13 @@ fun NotificationsScreen() {
|
||||
R.drawable.rider_pro_like,
|
||||
stringResource(R.string.like_upper)
|
||||
) {
|
||||
if (MessageListViewModel.likeNoticeCount > 0) {
|
||||
// 刷新点赞消息列表
|
||||
LikeNoticeViewModel.isFirstLoad = true
|
||||
// 清除点赞消息数量
|
||||
MessageListViewModel.clearLikeNoticeCount()
|
||||
}
|
||||
|
||||
navController.navigate(NavigationRoute.Likes.route)
|
||||
}
|
||||
NotificationIndicator(
|
||||
@@ -110,6 +120,11 @@ fun NotificationsScreen() {
|
||||
R.drawable.rider_pro_followers,
|
||||
stringResource(R.string.followers_upper)
|
||||
) {
|
||||
if (MessageListViewModel.followNoticeCount > 0) {
|
||||
// 刷新关注消息列表
|
||||
FollowerNoticeViewModel.isFirstLoad = true
|
||||
MessageListViewModel.clearFollowNoticeCount()
|
||||
}
|
||||
navController.navigate(NavigationRoute.Followers.route)
|
||||
}
|
||||
NotificationIndicator(
|
||||
@@ -117,6 +132,11 @@ fun NotificationsScreen() {
|
||||
R.drawable.rider_pro_favoriate,
|
||||
stringResource(R.string.favourites_upper)
|
||||
) {
|
||||
if (MessageListViewModel.favouriteNoticeCount > 0) {
|
||||
// 刷新收藏消息列表
|
||||
FavouriteNoticeViewModel.isFirstLoad = true
|
||||
MessageListViewModel.clearFavouriteNoticeCount()
|
||||
}
|
||||
navController.navigate(NavigationRoute.FavouritesScreen.route)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,15 @@ object MessageListViewModel : ViewModel() {
|
||||
private val _commentItemsFlow = MutableStateFlow<PagingData<CommentEntity>>(PagingData.empty())
|
||||
val commentItemsFlow = _commentItemsFlow.asStateFlow()
|
||||
var isLoading by mutableStateOf(false)
|
||||
|
||||
suspend fun initData() {
|
||||
var isFirstLoad = true
|
||||
suspend fun initData(force: Boolean = false) {
|
||||
if (!isFirstLoad && !force) {
|
||||
return
|
||||
}
|
||||
if (force) {
|
||||
isLoading = true
|
||||
}
|
||||
isFirstLoad = false
|
||||
val info = accountService.getMyNoticeInfo()
|
||||
noticeInfo = info
|
||||
viewModelScope.launch {
|
||||
@@ -43,7 +49,7 @@ object MessageListViewModel : ViewModel() {
|
||||
CommentPagingSource(
|
||||
CommentRemoteDataSource(commentService),
|
||||
selfNotice = true,
|
||||
order="latest"
|
||||
order = "latest"
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
@@ -51,6 +57,7 @@ object MessageListViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
|
||||
}
|
||||
|
||||
val likeNoticeCount
|
||||
@@ -80,4 +87,15 @@ object MessageListViewModel : ViewModel() {
|
||||
updateIsRead(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearLikeNoticeCount() {
|
||||
noticeInfo = noticeInfo?.copy(likeCount = 0)
|
||||
}
|
||||
|
||||
fun clearFollowNoticeCount() {
|
||||
noticeInfo = noticeInfo?.copy(followCount = 0)
|
||||
}
|
||||
fun clearFavouriteNoticeCount() {
|
||||
noticeInfo = noticeInfo?.copy(favoriteCount = 0)
|
||||
}
|
||||
}
|
||||
@@ -37,11 +37,16 @@ object MyProfileViewModel : ViewModel() {
|
||||
private var _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||
var momentsFlow = _momentsFlow.asStateFlow()
|
||||
var refreshing by mutableStateOf(false)
|
||||
var firstLoad = true
|
||||
fun loadProfile(pullRefresh: Boolean = false) {
|
||||
if (!firstLoad && !pullRefresh) {
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
if (pullRefresh){
|
||||
refreshing = true
|
||||
}
|
||||
firstLoad = false
|
||||
profile = accountService.getMyAccountProfile()
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
refreshing = false
|
||||
|
||||
@@ -23,8 +23,12 @@ object LikeNoticeViewModel : ViewModel() {
|
||||
private val accountService: AccountService = AccountServiceImpl()
|
||||
private val _likeItemsFlow = MutableStateFlow<PagingData<AccountLikeEntity>>(PagingData.empty())
|
||||
val likeItemsFlow = _likeItemsFlow.asStateFlow()
|
||||
|
||||
init {
|
||||
var isFirstLoad = true
|
||||
fun reload(force: Boolean = false) {
|
||||
if (!isFirstLoad && !force) {
|
||||
return
|
||||
}
|
||||
isFirstLoad = false
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
|
||||
@@ -56,8 +56,10 @@ fun LikeNoticeScreen() {
|
||||
var dataFlow = model.likeItemsFlow
|
||||
var likes = dataFlow.collectAsLazyPagingItems()
|
||||
LaunchedEffect(Unit) {
|
||||
model.reload()
|
||||
model.updateNotice()
|
||||
}
|
||||
|
||||
StatusBarMaskLayout(
|
||||
darkIcons = true,
|
||||
maskBoxBackgroundColor = Color(0xFFFFFFFF)
|
||||
@@ -125,7 +127,7 @@ fun ActionPostNoticeItem(
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
modifier = Modifier.padding(vertical = 16.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -188,7 +190,7 @@ fun LikeCommentNoticeItem(
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp).noRippleClickable {
|
||||
modifier = Modifier.padding(vertical = 16.dp).noRippleClickable {
|
||||
item.comment?.postId.let {
|
||||
navController.navigate(
|
||||
NavigationRoute.Post.route.replace(
|
||||
@@ -261,7 +263,8 @@ fun LikeCommentNoticeItem(
|
||||
Text(
|
||||
text = item.comment?.content ?: "",
|
||||
fontSize = 12.sp,
|
||||
color = Color(0x99000000)
|
||||
color = Color(0x99000000),
|
||||
maxLines = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,9 @@ import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun EmailSignupScreen() {
|
||||
var email by remember { mutableStateOf("takayamaaren@gmail.com") }
|
||||
var password by remember { mutableStateOf("Dzh17217.") }
|
||||
var confirmPassword by remember { mutableStateOf("Dzh17217.") }
|
||||
var email by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var confirmPassword by remember { mutableStateOf("") }
|
||||
var rememberMe by remember { mutableStateOf(false) }
|
||||
var acceptTerms by remember { mutableStateOf(false) }
|
||||
var acceptPromotions by remember { mutableStateOf(false) }
|
||||
@@ -68,7 +68,6 @@ fun EmailSignupScreen() {
|
||||
var confirmPasswordError by remember { mutableStateOf<String?>(null) }
|
||||
var termsError by remember { mutableStateOf<Boolean>(false) }
|
||||
var promotionsError by remember { mutableStateOf<Boolean>(false) }
|
||||
|
||||
fun validateForm(): Boolean {
|
||||
emailError = when {
|
||||
// 非空
|
||||
|
||||
@@ -93,9 +93,15 @@ fun UserAuthScreen() {
|
||||
}
|
||||
} catch (e: ServiceException) {
|
||||
// handle error
|
||||
|
||||
if (e.code == 12005) {
|
||||
emailError = context.getString(R.string.error_invalidate_username_password)
|
||||
passwordError = context.getString(R.string.error_invalidate_username_password)
|
||||
} else {
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -181,15 +187,12 @@ fun UserAuthScreen() {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
|
||||
Checkbox(
|
||||
com.aiosman.riderpro.ui.composables.Checkbox(
|
||||
checked = rememberMe,
|
||||
onCheckedChange = {
|
||||
rememberMe = it
|
||||
},
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = Color.Black
|
||||
),
|
||||
size = 18
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.remember_me),
|
||||
@@ -197,10 +200,34 @@ fun UserAuthScreen() {
|
||||
fontSize = 12.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(stringResource(R.string.forgot_password), fontSize = 12.sp, modifier = Modifier.noRippleClickable {
|
||||
Text(
|
||||
stringResource(R.string.forgot_password),
|
||||
fontSize = 12.sp,
|
||||
modifier = Modifier.noRippleClickable {
|
||||
navController.navigate(NavigationRoute.ResetPassword.route)
|
||||
})
|
||||
}
|
||||
)
|
||||
// CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
|
||||
// Checkbox(
|
||||
// checked = rememberMe,
|
||||
// onCheckedChange = {
|
||||
// rememberMe = it
|
||||
// },
|
||||
// colors = CheckboxDefaults.colors(
|
||||
// checkedColor = Color.Black
|
||||
// ),
|
||||
// )
|
||||
// Text(
|
||||
// stringResource(R.string.remember_me),
|
||||
// modifier = Modifier.padding(start = 8.dp),
|
||||
// fontSize = 12.sp
|
||||
// )
|
||||
// Spacer(modifier = Modifier.weight(1f))
|
||||
// Text(stringResource(R.string.forgot_password), fontSize = 12.sp, modifier = Modifier.noRippleClickable {
|
||||
// navController.navigate(NavigationRoute.ResetPassword.route)
|
||||
// })
|
||||
// }
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
ActionButton(
|
||||
|
||||
@@ -29,6 +29,7 @@ class CommentsViewModel(
|
||||
val commentsFlow = _commentsFlow.asStateFlow()
|
||||
var order: String by mutableStateOf("like")
|
||||
var addedCommentList by mutableStateOf<List<CommentEntity>>(emptyList())
|
||||
var subCommentLoadingMap by mutableStateOf(mutableMapOf<Int, Boolean>())
|
||||
|
||||
/**
|
||||
* 预加载,在跳转到 PostScreen 之前设置好内容
|
||||
@@ -162,6 +163,8 @@ class CommentsViewModel(
|
||||
val currentPagingData = commentsFlow.value
|
||||
val updatedPagingData = currentPagingData.map { comment ->
|
||||
if (comment.id == commentId) {
|
||||
try {
|
||||
subCommentLoadingMap[commentId] = true
|
||||
val subCommentList = commentService.getComments(
|
||||
postId = postId.toInt(),
|
||||
parentCommentId = commentId,
|
||||
@@ -172,6 +175,11 @@ class CommentsViewModel(
|
||||
reply = comment.reply.plus(subCommentList),
|
||||
replyPage = comment.replyPage + 1
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return@map comment.copy()
|
||||
} finally {
|
||||
subCommentLoadingMap[commentId] = false
|
||||
}
|
||||
}
|
||||
comment
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
@@ -47,7 +48,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -67,6 +67,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.AppState
|
||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||
@@ -79,22 +80,18 @@ import com.aiosman.riderpro.entity.MomentImageEntity
|
||||
import com.aiosman.riderpro.exp.formatPostTime
|
||||
import com.aiosman.riderpro.exp.timeAgo
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
|
||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.CustomClickableText
|
||||
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
|
||||
import com.aiosman.riderpro.ui.composables.FollowButton
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.paging.LoadState
|
||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.CustomClickableText
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -618,31 +615,12 @@ fun Header(
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = nickname ?: "", fontWeight = FontWeight.Bold)
|
||||
if (AppState.UserId != userId) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(20.dp)
|
||||
.wrapContentWidth()
|
||||
.padding(start = 6.dp)
|
||||
.noRippleClickable {
|
||||
onFollowClick()
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.height(18.dp).width(80.dp),
|
||||
painter = painterResource(id = R.drawable.follow_bg),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.FillWidth
|
||||
FollowButton(
|
||||
isFollowing = isFollowing,
|
||||
onFollowClick = onFollowClick,
|
||||
imageModifier = Modifier.height(18.dp).width(80.dp),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
Text(
|
||||
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(
|
||||
R.string.follow_upper
|
||||
),
|
||||
fontSize = 12.sp,
|
||||
color = Color.White,
|
||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (AppState.UserId == userId) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
@@ -757,28 +735,6 @@ fun PostDetails(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun LongPressClickableText(
|
||||
text: AnnotatedString,
|
||||
onClick: (Int) -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
style: TextStyle = TextStyle.Default
|
||||
) {
|
||||
ClickableText(
|
||||
text = text,
|
||||
onClick = onClick,
|
||||
style = style,
|
||||
modifier = Modifier.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
onLongClick()
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun CommentItem(
|
||||
@@ -910,7 +866,7 @@ fun CommentItem(
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
if (AppState.UserId?.toLong() != commentEntity.author) {
|
||||
Text(
|
||||
text = "Reply",
|
||||
text = stringResource(R.string.reply),
|
||||
fontSize = 12.sp,
|
||||
color = Color.Gray,
|
||||
modifier = Modifier.noRippleClickable {
|
||||
@@ -970,7 +926,7 @@ fun CommentItem(
|
||||
if (commentEntity.replyCount > 0 && !isChild && commentEntity.reply.size < commentEntity.replyCount) {
|
||||
val remaining = commentEntity.replyCount - commentEntity.reply.size
|
||||
Text(
|
||||
text = "View $remaining more replies",
|
||||
text = stringResource(R.string.view_more_reply, remaining),
|
||||
fontSize = 12.sp,
|
||||
color = Color(0xFF6F94AE),
|
||||
modifier = Modifier.noRippleClickable {
|
||||
@@ -1242,7 +1198,7 @@ fun CommentMenuModal(
|
||||
commentEntity?.let {
|
||||
Spacer(modifier = Modifier.width(48.dp))
|
||||
MenuActionItem(
|
||||
text = "Like",
|
||||
text = stringResource(R.string.like),
|
||||
content = {
|
||||
AnimatedLikeIcon(
|
||||
liked = it.liked,
|
||||
@@ -1258,7 +1214,7 @@ fun CommentMenuModal(
|
||||
Spacer(modifier = Modifier.width(48.dp))
|
||||
MenuActionItem(
|
||||
icon = R.drawable.rider_pro_comment,
|
||||
text = "Reply"
|
||||
text = stringResource(R.string.reply)
|
||||
) {
|
||||
onReplyClick()
|
||||
}
|
||||
|
||||
@@ -61,4 +61,13 @@
|
||||
<string name="original">原始图片</string>
|
||||
<string name="favourites">收藏</string>
|
||||
<string name="delete">删除</string>
|
||||
<string name="copy">复制</string>
|
||||
<string name="like">点赞</string>
|
||||
<string name="reply">回复</string>
|
||||
<string name="view_more_reply">查看更多%1d条回复</string>
|
||||
<string name="error_invalidate_username_password">错误的用户名或密码</string>
|
||||
<string name="recover_account_upper">找回密码</string>
|
||||
<string name="recover">找回</string>
|
||||
<string name="reset_mail_send_success">邮件已发送!请查收您的邮箱,按照邮件中的指示重置密码。</string>
|
||||
<string name="reset_mail_send_failed">邮件发送失败,请检查您的网络连接或稍后重试。</string>
|
||||
</resources>
|
||||
@@ -61,4 +61,12 @@
|
||||
<string name="favourites">Favourite</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="copy">Copy</string>
|
||||
<string name="like">Like</string>
|
||||
<string name="reply">Reply</string>
|
||||
<string name="view_more_reply">View %1d more replies</string>
|
||||
<string name="error_invalidate_username_password">Invalid email or password</string>
|
||||
<string name="recover_account_upper">RCOVER ACCOUNT</string>
|
||||
<string name="recover">Recover</string>
|
||||
<string name="reset_mail_send_success">An email has been sent to your registered email address. Please check your inbox and follow the instructions to reset your password.</string>
|
||||
<string name="reset_mail_send_failed">Failed to send email. Please check your network connection or try again later.</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user