更改推送
This commit is contained in:
@@ -13,6 +13,8 @@ data class ActionExtra(
|
|||||||
val action: String,
|
val action: String,
|
||||||
@SerializedName("postId")
|
@SerializedName("postId")
|
||||||
val postId: String?,
|
val postId: String?,
|
||||||
|
@SerializedName("commentId")
|
||||||
|
val commentId: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
class JpushReciver : JPushMessageReceiver() {
|
class JpushReciver : JPushMessageReceiver() {
|
||||||
@@ -30,12 +32,18 @@ class JpushReciver : JPushMessageReceiver() {
|
|||||||
val actionExtra = message.notificationExtras?.let {
|
val actionExtra = message.notificationExtras?.let {
|
||||||
gson.fromJson(it, ActionExtra::class.java)
|
gson.fromJson(it, ActionExtra::class.java)
|
||||||
}
|
}
|
||||||
actionExtra?.postId?.let {
|
|
||||||
val intent = Intent(context, MainActivity::class.java).apply {
|
val intent = Intent(context, MainActivity::class.java).apply {
|
||||||
putExtra("POST_ID", it)
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
}
|
}
|
||||||
|
actionExtra?.postId?.let {
|
||||||
|
intent.putExtra("POST_ID", it)
|
||||||
|
}
|
||||||
|
actionExtra?.commentId?.let {
|
||||||
|
intent.putExtra("COMMENT_ID", it)
|
||||||
|
}
|
||||||
|
actionExtra?.action?.let {
|
||||||
|
intent.putExtra("ACTION", it)
|
||||||
|
}
|
||||||
context?.startActivity(intent)
|
context?.startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,7 @@ import com.aiosman.riderpro.data.AccountServiceImpl
|
|||||||
import com.aiosman.riderpro.ui.Navigation
|
import com.aiosman.riderpro.ui.Navigation
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.post.NewPostViewModel
|
import com.aiosman.riderpro.ui.post.NewPostViewModel
|
||||||
|
import com.aiosman.riderpro.utils.Utils
|
||||||
import com.google.android.libraries.places.api.Places
|
import com.google.android.libraries.places.api.Places
|
||||||
import com.google.firebase.analytics.FirebaseAnalytics
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -58,6 +59,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
val accountService: AccountService = AccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
try {
|
try {
|
||||||
val resp = accountService.getMyAccount()
|
val resp = accountService.getMyAccount()
|
||||||
|
accountService.updateUserLanguage(
|
||||||
|
Utils.getCurrentLanguage()
|
||||||
|
)
|
||||||
// 设置当前登录用户 ID
|
// 设置当前登录用户 ID
|
||||||
AppState.UserId = resp.id
|
AppState.UserId = resp.id
|
||||||
return true
|
return true
|
||||||
@@ -105,20 +109,32 @@ class MainActivity : ComponentActivity() {
|
|||||||
var startDestination = NavigationRoute.Login.route
|
var startDestination = NavigationRoute.Login.route
|
||||||
// 如果有登录态,且记住登录状态,且账号有效,则初始化 FCM,下一步进入首页
|
// 如果有登录态,且记住登录状态,且账号有效,则初始化 FCM,下一步进入首页
|
||||||
if (AppStore.token != null && AppStore.rememberMe && isAccountValidate) {
|
if (AppStore.token != null && AppStore.rememberMe && isAccountValidate) {
|
||||||
Messaging.RegistDevice(scope,this@MainActivity)
|
Messaging.RegistDevice(scope, this@MainActivity)
|
||||||
startDestination = NavigationRoute.Index.route
|
startDestination = NavigationRoute.Index.route
|
||||||
}
|
}
|
||||||
setContent {
|
setContent {
|
||||||
Navigation(startDestination) { navController ->
|
Navigation(startDestination) { navController ->
|
||||||
// 处理带有 postId 的通知点击
|
// 处理带有 postId 的通知点击
|
||||||
val postId = intent.getStringExtra("POST_ID")
|
val postId = intent.getStringExtra("POST_ID")
|
||||||
|
var commentId = intent.getStringExtra("COMMENT_ID")
|
||||||
|
var action = intent.getStringExtra("ACTION")
|
||||||
|
if (action == "newFollow") {
|
||||||
|
navController.navigate(NavigationRoute.Followers.route)
|
||||||
|
return@Navigation
|
||||||
|
}
|
||||||
|
if (commentId == null) {
|
||||||
|
commentId = "0"
|
||||||
|
}
|
||||||
|
|
||||||
if (postId != null) {
|
if (postId != null) {
|
||||||
Log.d("MainActivity", "Navigation to Post$postId")
|
Log.d("MainActivity", "Navigation to Post$postId")
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route
|
||||||
|
.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
postId
|
postId
|
||||||
)
|
)
|
||||||
|
.replace("{highlightCommentId}", commentId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// 处理分享过来的图片
|
// 处理分享过来的图片
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.aiosman.riderpro.data.api.RegisterMessageChannelRequestBody
|
|||||||
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
||||||
import com.aiosman.riderpro.data.api.ResetPasswordRequestBody
|
import com.aiosman.riderpro.data.api.ResetPasswordRequestBody
|
||||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||||
|
import com.aiosman.riderpro.data.api.UpdateUserLangRequestBody
|
||||||
import com.aiosman.riderpro.entity.AccountFavouriteEntity
|
import com.aiosman.riderpro.entity.AccountFavouriteEntity
|
||||||
import com.aiosman.riderpro.entity.AccountLikeEntity
|
import com.aiosman.riderpro.entity.AccountLikeEntity
|
||||||
import com.aiosman.riderpro.entity.AccountProfileEntity
|
import com.aiosman.riderpro.entity.AccountProfileEntity
|
||||||
@@ -339,10 +340,20 @@ interface AccountService {
|
|||||||
*/
|
*/
|
||||||
suspend fun updateNotice(payload: UpdateNoticeRequestBody)
|
suspend fun updateNotice(payload: UpdateNoticeRequestBody)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册消息通道
|
||||||
|
*/
|
||||||
suspend fun registerMessageChannel(client: String, identifier: String)
|
suspend fun registerMessageChannel(client: String, identifier: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置密码
|
||||||
|
*/
|
||||||
suspend fun resetPassword(email: String)
|
suspend fun resetPassword(email: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户语言
|
||||||
|
*/
|
||||||
|
suspend fun updateUserLanguage(language: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountServiceImpl : AccountService {
|
class AccountServiceImpl : AccountService {
|
||||||
@@ -473,4 +484,8 @@ class AccountServiceImpl : AccountService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateUserLanguage(language: String) {
|
||||||
|
ApiClient.api.updateUserLang(UpdateUserLangRequestBody(language))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,13 @@ interface CommentService {
|
|||||||
* @param commentId 评论ID
|
* @param commentId 评论ID
|
||||||
*/
|
*/
|
||||||
suspend fun DeleteComment(commentId: Int)
|
suspend fun DeleteComment(commentId: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论
|
||||||
|
* @param commentId 评论ID
|
||||||
|
*/
|
||||||
|
suspend fun getCommentById(commentId: Int): CommentEntity
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,7 +127,6 @@ data class Comment(
|
|||||||
comment = content,
|
comment = content,
|
||||||
date = ApiClient.dateFromApiString(createdAt),
|
date = ApiClient.dateFromApiString(createdAt),
|
||||||
likes = likeCount,
|
likes = likeCount,
|
||||||
replies = emptyList(),
|
|
||||||
postId = postId,
|
postId = postId,
|
||||||
avatar = "${ApiClient.BASE_SERVER}${user.avatar}",
|
avatar = "${ApiClient.BASE_SERVER}${user.avatar}",
|
||||||
author = user.id,
|
author = user.id,
|
||||||
@@ -240,4 +246,10 @@ class CommentServiceImpl : CommentService {
|
|||||||
val resp = ApiClient.api.deleteComment(commentId)
|
val resp = ApiClient.api.deleteComment(commentId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getCommentById(commentId: Int): CommentEntity {
|
||||||
|
val resp = ApiClient.api.getComment(commentId)
|
||||||
|
val body = resp.body() ?: throw ServiceException("Failed to get comment")
|
||||||
|
return body.data.toCommentEntity()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +98,11 @@ data class ResetPasswordRequestBody(
|
|||||||
val username: String,
|
val username: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class UpdateUserLangRequestBody(
|
||||||
|
@SerializedName("language")
|
||||||
|
val lang: String,
|
||||||
|
)
|
||||||
|
|
||||||
interface RiderProAPI {
|
interface RiderProAPI {
|
||||||
@POST("register")
|
@POST("register")
|
||||||
suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
|
suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
|
||||||
@@ -280,4 +285,15 @@ interface RiderProAPI {
|
|||||||
suspend fun resetPassword(
|
suspend fun resetPassword(
|
||||||
@Body body: ResetPasswordRequestBody
|
@Body body: ResetPasswordRequestBody
|
||||||
): Response<Unit>
|
): Response<Unit>
|
||||||
|
|
||||||
|
@GET("comment/{id}")
|
||||||
|
suspend fun getComment(
|
||||||
|
@Path("id") id: Int
|
||||||
|
): Response<DataContainer<Comment>>
|
||||||
|
|
||||||
|
@PATCH("account/my/lang")
|
||||||
|
suspend fun updateUserLang(
|
||||||
|
@Body body: UpdateUserLangRequestBody
|
||||||
|
): Response<Unit>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,6 @@ data class CommentEntity(
|
|||||||
val comment: String,
|
val comment: String,
|
||||||
val date: Date,
|
val date: Date,
|
||||||
val likes: Int,
|
val likes: Int,
|
||||||
val replies: List<CommentEntity>,
|
|
||||||
val postId: Int = 0,
|
val postId: Int = 0,
|
||||||
val avatar: String,
|
val avatar: String,
|
||||||
val author: Long,
|
val author: Long,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ sealed class NavigationRoute(
|
|||||||
data object LocationDetail : NavigationRoute("LocationDetail/{x}/{y}")
|
data object LocationDetail : NavigationRoute("LocationDetail/{x}/{y}")
|
||||||
data object OfficialPhoto : NavigationRoute("OfficialPhoto")
|
data object OfficialPhoto : NavigationRoute("OfficialPhoto")
|
||||||
data object OfficialPhotographer : NavigationRoute("OfficialPhotographer")
|
data object OfficialPhotographer : NavigationRoute("OfficialPhotographer")
|
||||||
data object Post : NavigationRoute("Post/{id}")
|
data object Post : NavigationRoute("Post/{id}/{highlightCommentId}")
|
||||||
data object ModificationList : NavigationRoute("ModificationList")
|
data object ModificationList : NavigationRoute("ModificationList")
|
||||||
data object MyMessage : NavigationRoute("MyMessage")
|
data object MyMessage : NavigationRoute("MyMessage")
|
||||||
data object Comments : NavigationRoute("Comments")
|
data object Comments : NavigationRoute("Comments")
|
||||||
@@ -135,14 +135,21 @@ fun NavigationController(
|
|||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
route = NavigationRoute.Post.route,
|
route = NavigationRoute.Post.route,
|
||||||
arguments = listOf(navArgument("id") { type = NavType.StringType }),
|
arguments = listOf(
|
||||||
|
navArgument("id") { type = NavType.StringType },
|
||||||
|
navArgument("highlightCommentId") { type = NavType.IntType }
|
||||||
|
),
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalAnimatedContentScope provides this,
|
LocalAnimatedContentScope provides this,
|
||||||
) {
|
) {
|
||||||
val id = backStackEntry.arguments?.getString("id")
|
val id = backStackEntry.arguments?.getString("id")
|
||||||
|
val highlightCommentId = backStackEntry.arguments?.getInt("highlightCommentId")?.let {
|
||||||
|
if (it == 0) null else it
|
||||||
|
}
|
||||||
PostScreen(
|
PostScreen(
|
||||||
id!!
|
id!!,
|
||||||
|
highlightCommentId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ fun FavouriteListPage() {
|
|||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
momentItem.id.toString()
|
momentItem.id.toString()
|
||||||
)
|
).replace("{highlightCommentId}", "0")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ fun NotificationsScreen() {
|
|||||||
// 清除点赞消息数量
|
// 清除点赞消息数量
|
||||||
MessageListViewModel.clearLikeNoticeCount()
|
MessageListViewModel.clearLikeNoticeCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
navController.navigate(NavigationRoute.Likes.route)
|
navController.navigate(NavigationRoute.Likes.route)
|
||||||
}
|
}
|
||||||
NotificationIndicator(
|
NotificationIndicator(
|
||||||
@@ -147,8 +146,7 @@ fun NotificationsScreen() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(bottom = 48.dp)
|
.padding(bottom = 48.dp),
|
||||||
,
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -177,10 +175,17 @@ fun NotificationsScreen() {
|
|||||||
CommentNoticeItem(comment) {
|
CommentNoticeItem(comment) {
|
||||||
MessageListViewModel.updateReadStatus(comment.id)
|
MessageListViewModel.updateReadStatus(comment.id)
|
||||||
MessageListViewModel.viewModelScope.launch {
|
MessageListViewModel.viewModelScope.launch {
|
||||||
|
var highlightCommentId = comment.id
|
||||||
|
comment.parentCommentId?.let {
|
||||||
|
highlightCommentId = it
|
||||||
|
}
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
comment.postId.toString()
|
comment.postId.toString()
|
||||||
|
).replace(
|
||||||
|
"{highlightCommentId}",
|
||||||
|
highlightCommentId.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -387,8 +392,12 @@ fun CommentNoticeItem(
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
Row {
|
Row {
|
||||||
|
var text = commentItem.comment
|
||||||
|
if (commentItem.parentCommentId != null) {
|
||||||
|
text = "Reply you: $text"
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = commentItem.comment,
|
text = text,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
color = Color(0x99000000),
|
color = Color(0x99000000),
|
||||||
@@ -406,6 +415,10 @@ fun CommentNoticeItem(
|
|||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.width(24.dp))
|
Spacer(modifier = Modifier.width(24.dp))
|
||||||
commentItem.post?.let {
|
commentItem.post?.let {
|
||||||
|
Box {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.padding(4.dp)
|
||||||
|
) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
context = context,
|
context = context,
|
||||||
imageUrl = it.images[0].thumbnail,
|
imageUrl = it.images[0].thumbnail,
|
||||||
@@ -413,6 +426,21 @@ fun CommentNoticeItem(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
)
|
)
|
||||||
|
// unread indicator
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commentItem.unread) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(Color(0xFFE53935), CircleShape)
|
||||||
|
.size(12.dp)
|
||||||
|
.align(Alignment.TopEnd)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ object MessageListViewModel : ViewModel() {
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
commentService.updateReadStatus(id)
|
commentService.updateReadStatus(id)
|
||||||
updateIsRead(id)
|
updateIsRead(id)
|
||||||
|
updateUnReadCount(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,4 +99,9 @@ object MessageListViewModel : ViewModel() {
|
|||||||
fun clearFavouriteNoticeCount() {
|
fun clearFavouriteNoticeCount() {
|
||||||
noticeInfo = noticeInfo?.copy(favoriteCount = 0)
|
noticeInfo = noticeInfo?.copy(favoriteCount = 0)
|
||||||
}
|
}
|
||||||
|
fun updateUnReadCount(delta : Int) {
|
||||||
|
noticeInfo?.let {
|
||||||
|
noticeInfo = it.copy(commentCount = it.commentCount + delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -175,8 +175,11 @@ fun MomentCard(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
// PostViewModel.preTransit(momentEntity)
|
navController.navigate(
|
||||||
navController.navigate("Post/${momentEntity.id}")
|
route = NavigationRoute.Post.route
|
||||||
|
.replace("{id}", momentEntity.id.toString())
|
||||||
|
.replace("{highlightCommentId}", "0")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
MomentContentGroup(
|
MomentContentGroup(
|
||||||
|
|||||||
@@ -813,7 +813,7 @@ fun MomentCardPicture(imageUrl: String, momentEntity: MomentEntity) {
|
|||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
momentEntity.id.toString()
|
momentEntity.id.toString()
|
||||||
)
|
).replace("{highlightCommentId}", "0")
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ fun DiscoverView() {
|
|||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
momentItem.id.toString()
|
momentItem.id.toString()
|
||||||
)
|
).replace("{highlightCommentId}", "0")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ fun ActionPostNoticeItem(
|
|||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
postId.toString()
|
postId.toString()
|
||||||
)
|
).replace("{highlightCommentId}", "0")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -196,7 +196,7 @@ fun LikeCommentNoticeItem(
|
|||||||
NavigationRoute.Post.route.replace(
|
NavigationRoute.Post.route.replace(
|
||||||
"{id}",
|
"{id}",
|
||||||
it.toString()
|
it.toString()
|
||||||
)
|
).replace("{highlightCommentId}", "0")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import com.aiosman.riderpro.ui.composables.ActionButton
|
|||||||
import com.aiosman.riderpro.ui.composables.CheckboxWithLabel
|
import com.aiosman.riderpro.ui.composables.CheckboxWithLabel
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||||
import com.aiosman.riderpro.ui.composables.TextInputField
|
import com.aiosman.riderpro.ui.composables.TextInputField
|
||||||
|
import com.aiosman.riderpro.utils.Utils
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -155,6 +156,9 @@ fun EmailSignupScreen() {
|
|||||||
try {
|
try {
|
||||||
val resp = accountService.getMyAccount()
|
val resp = accountService.getMyAccount()
|
||||||
AppState.UserId = resp.id
|
AppState.UserId = resp.id
|
||||||
|
accountService.updateUserLanguage(
|
||||||
|
Utils.getCurrentLanguage()
|
||||||
|
)
|
||||||
Messaging.RegistDevice(scope, context)
|
Messaging.RegistDevice(scope, context)
|
||||||
} catch (e: ServiceException) {
|
} catch (e: ServiceException) {
|
||||||
scope.launch(Dispatchers.Main) {
|
scope.launch(Dispatchers.Main) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import com.aiosman.riderpro.ui.NavigationRoute
|
|||||||
import com.aiosman.riderpro.ui.composables.ActionButton
|
import com.aiosman.riderpro.ui.composables.ActionButton
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.aiosman.riderpro.utils.GoogleLogin
|
import com.aiosman.riderpro.utils.GoogleLogin
|
||||||
|
import com.aiosman.riderpro.utils.Utils
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -84,6 +85,9 @@ fun SignupScreen() {
|
|||||||
// 获取token 信息
|
// 获取token 信息
|
||||||
try {
|
try {
|
||||||
val resp = accountService.getMyAccount()
|
val resp = accountService.getMyAccount()
|
||||||
|
accountService.updateUserLanguage(
|
||||||
|
Utils.getCurrentLanguage()
|
||||||
|
)
|
||||||
AppState.UserId = resp.id
|
AppState.UserId = resp.id
|
||||||
Messaging.RegistDevice(coroutineScope, context)
|
Messaging.RegistDevice(coroutineScope, context)
|
||||||
} catch (e: ServiceException) {
|
} catch (e: ServiceException) {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class CommentsViewModel(
|
|||||||
var order: String by mutableStateOf("like")
|
var order: String by mutableStateOf("like")
|
||||||
var addedCommentList by mutableStateOf<List<CommentEntity>>(emptyList())
|
var addedCommentList by mutableStateOf<List<CommentEntity>>(emptyList())
|
||||||
var subCommentLoadingMap by mutableStateOf(mutableMapOf<Int, Boolean>())
|
var subCommentLoadingMap by mutableStateOf(mutableMapOf<Int, Boolean>())
|
||||||
|
var highlightCommentId by mutableStateOf<Int?>(null)
|
||||||
|
var highlightComment by mutableStateOf<CommentEntity?>(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预加载,在跳转到 PostScreen 之前设置好内容
|
* 预加载,在跳转到 PostScreen 之前设置好内容
|
||||||
@@ -49,6 +51,9 @@ class CommentsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载评论
|
||||||
|
*/
|
||||||
fun reloadComment() {
|
fun reloadComment() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
@@ -68,18 +73,75 @@ class CommentsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun likeComment(commentId: Int) {
|
|
||||||
commentService.likeComment(commentId)
|
suspend fun highlightComment(commentId: Int) {
|
||||||
|
highlightCommentId = commentId
|
||||||
|
val resp = commentService.getCommentById(commentId)
|
||||||
|
highlightComment = resp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新高亮评论点赞状态
|
||||||
|
*/
|
||||||
|
private fun updateHighlightCommentLike(commentId: Int, isLike: Boolean): Boolean {
|
||||||
|
var isUpdate = false
|
||||||
|
highlightComment?.let {
|
||||||
|
if (it.id == commentId) {
|
||||||
|
highlightComment =
|
||||||
|
it.copy(liked = isLike, likes = if (isLike) it.likes + 1 else it.likes - 1)
|
||||||
|
isUpdate = true
|
||||||
|
}
|
||||||
|
highlightComment = it.copy(
|
||||||
|
reply = it.reply.map { replyComment ->
|
||||||
|
if (replyComment.id == commentId) {
|
||||||
|
isUpdate = true
|
||||||
|
replyComment.copy(
|
||||||
|
liked = isLike,
|
||||||
|
likes = if (isLike) replyComment.likes + 1 else replyComment.likes - 1
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
replyComment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return isUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新添加的评论点赞状态
|
||||||
|
*/
|
||||||
|
private fun updateAddedCommentLike(commentId: Int, isLike: Boolean): Boolean {
|
||||||
|
var isUpdate = false
|
||||||
|
addedCommentList = addedCommentList.map {
|
||||||
|
if (it.id == commentId) {
|
||||||
|
isUpdate = true
|
||||||
|
it.copy(liked = isLike, likes = if (isLike) it.likes + 1 else it.likes - 1)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新评论点赞状态
|
||||||
|
*/
|
||||||
|
private fun updateCommentLike(commentId: Int, isLike: Boolean) {
|
||||||
val currentPagingData = commentsFlow.value
|
val currentPagingData = commentsFlow.value
|
||||||
val updatedPagingData = currentPagingData.map { comment ->
|
val updatedPagingData = currentPagingData.map { comment ->
|
||||||
if (comment.id == commentId) {
|
if (comment.id == commentId) {
|
||||||
comment.copy(liked = !comment.liked, likes = comment.likes + 1)
|
comment.copy(
|
||||||
|
liked = isLike,
|
||||||
|
likes = if (isLike) comment.likes + 1 else comment.likes - 1
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// 可能是回复的评论
|
// 可能是回复的评论
|
||||||
comment.copy(reply = comment.reply.map { replyComment ->
|
comment.copy(reply = comment.reply.map { replyComment ->
|
||||||
if (replyComment.id == commentId) {
|
if (replyComment.id == commentId) {
|
||||||
replyComment.copy(
|
replyComment.copy(
|
||||||
liked = !replyComment.liked, likes = replyComment.likes + 1
|
liked = isLike,
|
||||||
|
likes = if (isLike) replyComment.likes + 1 else replyComment.likes - 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
replyComment
|
replyComment
|
||||||
@@ -88,45 +150,40 @@ class CommentsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_commentsFlow.value = updatedPagingData
|
_commentsFlow.value = updatedPagingData
|
||||||
// 更新addCommentList
|
|
||||||
addedCommentList = addedCommentList.map {
|
|
||||||
if (it.id == commentId) {
|
|
||||||
it.copy(liked = !it.liked, likes = it.likes + 1)
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞评论
|
||||||
|
*/
|
||||||
|
suspend fun likeComment(commentId: Int) {
|
||||||
|
try {
|
||||||
|
commentService.likeComment(commentId)
|
||||||
|
// 更新addCommentList
|
||||||
|
if (updateHighlightCommentLike(commentId, true)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (updateAddedCommentLike(commentId, true)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateCommentLike(commentId, true)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消点赞评论
|
||||||
suspend fun unlikeComment(commentId: Int) {
|
suspend fun unlikeComment(commentId: Int) {
|
||||||
commentService.dislikeComment(commentId)
|
commentService.dislikeComment(commentId)
|
||||||
val currentPagingData = commentsFlow.value
|
// 更新高亮评论点赞状态
|
||||||
val updatedPagingData = currentPagingData.map { comment ->
|
if (updateHighlightCommentLike(commentId, false)) {
|
||||||
if (comment.id == commentId) {
|
return
|
||||||
comment.copy(liked = !comment.liked, likes = comment.likes - 1)
|
|
||||||
} else {
|
|
||||||
// 可能是回复的评论
|
|
||||||
comment.copy(reply = comment.reply.map { replyComment ->
|
|
||||||
if (replyComment.id == commentId) {
|
|
||||||
replyComment.copy(
|
|
||||||
liked = !replyComment.liked, likes = replyComment.likes - 1
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
replyComment
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_commentsFlow.value = updatedPagingData
|
|
||||||
|
|
||||||
// 更新addCommentList
|
|
||||||
addedCommentList = addedCommentList.map {
|
|
||||||
if (it.id == commentId) {
|
|
||||||
it.copy(liked = !it.liked, likes = it.likes - 1)
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
}
|
||||||
|
// 更新添加的评论点赞状态
|
||||||
|
if (updateAddedCommentLike(commentId, false)) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
// 更新评论点赞状态
|
||||||
|
updateCommentLike(commentId, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createComment(
|
suspend fun createComment(
|
||||||
@@ -163,6 +220,24 @@ class CommentsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loadMoreSubComments(commentId: Int) {
|
fun loadMoreSubComments(commentId: Int) {
|
||||||
|
if (highlightComment?.id == commentId) {
|
||||||
|
// 高亮的评论,更新高亮评论的回复
|
||||||
|
highlightComment?.let {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val subCommentList = commentService.getComments(
|
||||||
|
postId = postId.toInt(),
|
||||||
|
parentCommentId = commentId,
|
||||||
|
pageNumber = it.replyPage + 1,
|
||||||
|
pageSize = 3,
|
||||||
|
).list
|
||||||
|
highlightComment = it.copy(
|
||||||
|
reply = it.reply.plus(subCommentList),
|
||||||
|
replyPage = it.replyPage + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 普通评论
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val currentPagingData = commentsFlow.value
|
val currentPagingData = commentsFlow.value
|
||||||
val updatedPagingData = currentPagingData.map { comment ->
|
val updatedPagingData = currentPagingData.map { comment ->
|
||||||
@@ -174,6 +249,7 @@ class CommentsViewModel(
|
|||||||
parentCommentId = commentId,
|
parentCommentId = commentId,
|
||||||
pageNumber = comment.replyPage + 1,
|
pageNumber = comment.replyPage + 1,
|
||||||
pageSize = 3,
|
pageSize = 3,
|
||||||
|
order = "earliest"
|
||||||
).list
|
).list
|
||||||
return@map comment.copy(
|
return@map comment.copy(
|
||||||
reply = comment.reply.plus(subCommentList),
|
reply = comment.reply.plus(subCommentList),
|
||||||
@@ -190,4 +266,6 @@ class CommentsViewModel(
|
|||||||
_commentsFlow.value = updatedPagingData
|
_commentsFlow.value = updatedPagingData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ package com.aiosman.riderpro.ui.post
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.spring
|
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
@@ -28,7 +27,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
@@ -51,7 +49,6 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.focusModifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
@@ -103,6 +100,7 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun PostScreen(
|
fun PostScreen(
|
||||||
id: String,
|
id: String,
|
||||||
|
highlightCommentId: Int?
|
||||||
) {
|
) {
|
||||||
val viewModel = viewModel<PostViewModel>(
|
val viewModel = viewModel<PostViewModel>(
|
||||||
key = "PostViewModel_$id",
|
key = "PostViewModel_$id",
|
||||||
@@ -124,7 +122,11 @@ fun PostScreen(
|
|||||||
skipPartiallyExpanded = true
|
skipPartiallyExpanded = true
|
||||||
)
|
)
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.initData()
|
viewModel.initData(
|
||||||
|
highlightCommentId = highlightCommentId.let {
|
||||||
|
if (it == 0) null else it
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showCommentMenu) {
|
if (showCommentMenu) {
|
||||||
@@ -147,7 +149,6 @@ fun PostScreen(
|
|||||||
contextComment?.let {
|
contextComment?.let {
|
||||||
viewModel.deleteComment(it.id)
|
viewModel.deleteComment(it.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
commentEntity = contextComment,
|
commentEntity = contextComment,
|
||||||
onCloseClick = {
|
onCloseClick = {
|
||||||
@@ -405,7 +406,44 @@ fun CommentContent(
|
|||||||
val addedTopLevelComment = viewModel.addedCommentList.filter {
|
val addedTopLevelComment = viewModel.addedCommentList.filter {
|
||||||
it.parentCommentId == null
|
it.parentCommentId == null
|
||||||
}
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.animateContentSize(animationSpec = tween(durationMillis = 500))
|
||||||
|
) {
|
||||||
|
viewModel.highlightComment?.let {
|
||||||
|
CommentItem(
|
||||||
|
it,
|
||||||
|
onLike = { comment ->
|
||||||
|
viewModel.viewModelScope.launch {
|
||||||
|
if (comment.liked) {
|
||||||
|
viewModel.unlikeComment(comment.id)
|
||||||
|
} else {
|
||||||
|
viewModel.likeComment(comment.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongClick = { comment ->
|
||||||
|
onLongClick(comment)
|
||||||
|
},
|
||||||
|
onReply = { parentComment, _, _, _ ->
|
||||||
|
onReply(
|
||||||
|
parentComment,
|
||||||
|
parentComment.author,
|
||||||
|
parentComment.name,
|
||||||
|
parentComment.avatar
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onLoadMoreSubComments = {
|
||||||
|
viewModel.viewModelScope.launch {
|
||||||
|
viewModel.loadMoreSubComments(it.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
@@ -454,6 +492,7 @@ fun CommentContent(
|
|||||||
|
|
||||||
for (idx in 0 until commentsPagging.itemCount) {
|
for (idx in 0 until commentsPagging.itemCount) {
|
||||||
val item = commentsPagging[idx] ?: return
|
val item = commentsPagging[idx] ?: return
|
||||||
|
if (item.id != viewModel.highlightCommentId) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = true,
|
visible = true,
|
||||||
enter = slideInVertically(),
|
enter = slideInVertically(),
|
||||||
@@ -495,6 +534,8 @@ fun CommentContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Handle loading and error states as before
|
// Handle loading and error states as before
|
||||||
if (commentsPagging.loadState.refresh is LoadState.Loading) {
|
if (commentsPagging.loadState.refresh is LoadState.Loading) {
|
||||||
Box(
|
Box(
|
||||||
@@ -932,7 +973,9 @@ fun CommentItem(
|
|||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(start = 12.dp + 40.dp).animateContentSize(
|
modifier = Modifier
|
||||||
|
.padding(start = 12.dp + 40.dp)
|
||||||
|
.animateContentSize(
|
||||||
animationSpec = tween(
|
animationSpec = tween(
|
||||||
durationMillis = 500
|
durationMillis = 500
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,20 +29,28 @@ class PostViewModel(
|
|||||||
var accountService: AccountService = AccountServiceImpl()
|
var accountService: AccountService = AccountServiceImpl()
|
||||||
var commentsViewModel: CommentsViewModel = CommentsViewModel(postId)
|
var commentsViewModel: CommentsViewModel = CommentsViewModel(postId)
|
||||||
var isError by mutableStateOf(false)
|
var isError by mutableStateOf(false)
|
||||||
|
var isFirstLoad by mutableStateOf(true)
|
||||||
|
|
||||||
fun reloadComment() {
|
fun reloadComment() {
|
||||||
commentsViewModel.reloadComment()
|
commentsViewModel.reloadComment()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun initData() {
|
suspend fun initData(highlightCommentId: Int? = null) {
|
||||||
|
if (!isFirstLoad) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isFirstLoad = false
|
||||||
try {
|
try {
|
||||||
moment = service.getMomentById(postId.toInt())
|
moment = service.getMomentById(postId.toInt())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
isError = true
|
isError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
highlightCommentId?.let {
|
||||||
|
commentsViewModel.highlightComment(it)
|
||||||
|
}
|
||||||
commentsViewModel.reloadComment()
|
commentsViewModel.reloadComment()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun likeComment(commentId: Int) {
|
suspend fun likeComment(commentId: Int) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import coil.request.CachePolicy
|
|||||||
import com.aiosman.riderpro.data.api.AuthInterceptor
|
import com.aiosman.riderpro.data.api.AuthInterceptor
|
||||||
import com.aiosman.riderpro.data.api.getUnsafeOkHttpClient
|
import com.aiosman.riderpro.data.api.getUnsafeOkHttpClient
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
@@ -56,4 +57,8 @@ object Utils {
|
|||||||
else -> "$years years ago"
|
else -> "$years years ago"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCurrentLanguage(): String {
|
||||||
|
return Locale.getDefault().language
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user