调整细节

This commit is contained in:
2024-09-15 18:26:08 +08:00
parent 4b28a882a9
commit 6cf8e740dd
23 changed files with 341 additions and 156 deletions

View File

@@ -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")
}
}
}

View File

@@ -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()

View File

@@ -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)
)
}
}

View File

@@ -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)
}
}
}
}
}
}

View File

@@ -30,6 +30,7 @@ fun FavouriteNoticeScreen() {
var dataFlow = model.favouriteItemsFlow
var favourites = dataFlow.collectAsLazyPagingItems()
LaunchedEffect(Unit) {
model.reload()
model.updateNotice()
}
StatusBarMaskLayout(

View File

@@ -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),

View File

@@ -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)
}
}
@@ -55,3 +58,4 @@ fun FollowerListScreen(userId: Int) {
}
}
}
}

View File

@@ -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)
}
}

View File

@@ -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
// )
// )
// }
}
}

View File

@@ -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),

View File

@@ -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)
}
}
@@ -55,3 +58,4 @@ fun FollowingListScreen(userId: Int) {
}
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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 {
@@ -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)
}
}

View File

@@ -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

View File

@@ -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),

View File

@@ -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
)
}
}

View File

@@ -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 {
// 非空

View File

@@ -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(

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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>

View File

@@ -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>