新增我的页面下拉刷新功能

新增个人主页为空时的文案

发布动态后自动刷新个人主页
This commit is contained in:
2024-09-06 16:16:20 +08:00
parent fc324240b6
commit f357e12f7c
6 changed files with 163 additions and 157 deletions

View File

@@ -30,21 +30,7 @@ object MomentViewModel : ViewModel() {
val accountService: AccountService = AccountServiceImpl()
var existsException = mutableStateOf(false)
init {
viewModelScope.launch {
// 获取当前用户信息
val profile = accountService.getMyAccountProfile()
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
timelineId = profile.id
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it
}
}
refreshPager()
}
fun refreshPager() {
viewModelScope.launch {

View File

@@ -5,6 +5,7 @@ import android.net.Uri
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -32,14 +33,18 @@ import kotlinx.coroutines.launch
object MyProfileViewModel : ViewModel() {
val accountService: AccountService = AccountServiceImpl()
val momentService: MomentService = MomentServiceImpl()
val userService = UserServiceImpl()
var profile by mutableStateOf<AccountProfileEntity?>(null)
private var _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
var momentsFlow = _momentsFlow.asStateFlow()
fun loadProfile() {
var refreshing by mutableStateOf(false)
fun loadProfile(pullRefresh: Boolean = false) {
viewModelScope.launch {
if (pullRefresh){
refreshing = true
}
profile = accountService.getMyAccountProfile()
val profile = accountService.getMyAccountProfile()
refreshing = false
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
@@ -51,6 +56,7 @@ object MyProfileViewModel : ViewModel() {
).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it
}
}
}

View File

@@ -2,16 +2,12 @@ package com.aiosman.riderpro.ui.index.tabs.profile
import android.app.Activity
import android.content.Intent
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -22,7 +18,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -33,13 +28,14 @@ import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -71,23 +67,19 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.LocalSharedTransitionScope
import com.aiosman.riderpro.R
import com.aiosman.riderpro.entity.AccountProfileEntity
import com.aiosman.riderpro.exp.formatPostTime
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.exp.formatPostTime2
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.index.tabs.moment.MomentCard
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.ui.post.PostViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ProfilePage() {
val model = MyProfileViewModel
@@ -98,6 +90,9 @@ fun ProfilePage() {
val moments = model.momentsFlow.collectAsLazyPagingItems()
val navController: NavController = LocalNavController.current
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.loadProfile(pullRefresh = true)
})
val context = LocalContext.current
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
val pickBannerImageLauncher = rememberLauncherForActivityResult(
@@ -110,7 +105,7 @@ fun ProfilePage() {
}
}
}
LazyColumn(
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5))
@@ -120,148 +115,154 @@ fun ProfilePage() {
.toDp() + 48.dp
da
})
.pullRefresh(state)
) {
item {
Box(
modifier = Modifier
.fillMaxWidth()
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
Box(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.noRippleClickable {
Intent(Intent.ACTION_PICK).apply {
type = "image/*"
pickBannerImageLauncher.launch(this)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.noRippleClickable {
Intent(Intent.ACTION_PICK).apply {
type = "image/*"
pickBannerImageLauncher.launch(this)
}
}
}
) {
val banner = model.profile?.banner
if (banner != null) {
CustomAsyncImage(
LocalContext.current,
banner,
modifier = Modifier
.fillMaxSize(),
contentDescription = "",
contentScale = ContentScale.Crop
)
} else {
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
modifier = Modifier
.fillMaxSize(),
contentDescription = "",
contentScale = ContentScale.Crop
)
}
}
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(
top = statusBarPaddingValues.calculateTopPadding(),
start = 16.dp,
end = 16.dp
)
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "",
modifier = Modifier.noRippleClickable {
expanded = true
},
tint = Color.White
)
MaterialTheme(
shapes = MaterialTheme.shapes.copy(
extraSmall = RoundedCornerShape(
16.dp
)
)
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.width(250.dp)
.background(Color.White)
) {
Box(
val banner = model.profile?.banner
if (banner != null) {
CustomAsyncImage(
LocalContext.current,
banner,
modifier = Modifier
.padding(vertical = 14.dp, horizontal = 24.dp)
.noRippleClickable {
expanded = false
scope.launch {
model.logout()
navController.navigate(NavigationRoute.Login.route) {
popUpTo(NavigationRoute.Index.route) {
inclusive = true
.fillMaxSize(),
contentDescription = "",
contentScale = ContentScale.Crop
)
} else {
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
modifier = Modifier
.fillMaxSize(),
contentDescription = "",
contentScale = ContentScale.Crop
)
}
}
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(
top = statusBarPaddingValues.calculateTopPadding(),
start = 16.dp,
end = 16.dp
)
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "",
modifier = Modifier.noRippleClickable {
expanded = true
},
tint = Color.White
)
MaterialTheme(
shapes = MaterialTheme.shapes.copy(
extraSmall = RoundedCornerShape(
16.dp
)
)
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.width(250.dp)
.background(Color.White)
) {
Box(
modifier = Modifier
.padding(vertical = 14.dp, horizontal = 24.dp)
.noRippleClickable {
expanded = false
scope.launch {
model.logout()
navController.navigate(NavigationRoute.Login.route) {
popUpTo(NavigationRoute.Index.route) {
inclusive = true
}
}
}
}
}) {
Row {
Text("Logout", fontWeight = FontWeight.W500)
Spacer(modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.mipmap.rider_pro_logout),
contentDescription = "",
modifier = Modifier.size(24.dp)
)
}) {
Row {
Text(stringResource(R.string.logout), fontWeight = FontWeight.W500)
Spacer(modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.mipmap.rider_pro_logout),
contentDescription = "",
modifier = Modifier.size(24.dp)
)
}
}
}
Box(
modifier = Modifier
.padding(vertical = 14.dp, horizontal = 24.dp)
.noRippleClickable {
expanded = false
scope.launch {
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
}
}) {
Row {
Text("Change password", fontWeight = FontWeight.W500)
Spacer(modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.mipmap.rider_pro_change_password),
contentDescription = "",
modifier = Modifier.size(24.dp)
)
Box(
modifier = Modifier
.padding(vertical = 14.dp, horizontal = 24.dp)
.noRippleClickable {
expanded = false
scope.launch {
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
}
}) {
Row {
Text(stringResource(R.string.change_password), fontWeight = FontWeight.W500)
Spacer(modifier = Modifier.weight(1f))
Icon(
painter = painterResource(id = R.mipmap.rider_pro_change_password),
contentDescription = "",
modifier = Modifier.size(24.dp)
)
}
}
}
}
}
}
Spacer(modifier = Modifier.height(32.dp))
model.profile?.let {
UserInformation(
accountProfileEntity = it,
onEditProfileClick = {
navController.navigate(NavigationRoute.AccountEdit.route)
}
)
}
if (moments.itemCount == 0) {
EmptyMomentPostUnit()
}
}
Spacer(modifier = Modifier.height(32.dp))
model.profile?.let {
UserInformation(
accountProfileEntity = it,
onEditProfileClick = {
navController.navigate(NavigationRoute.AccountEdit.route)
}
)
}
if (moments.itemCount == 0) {
EmptyMomentPostUnit()
}
}
items(moments.itemCount) { idx ->
val momentItem = moments[idx] ?: return@items
MomentPostUnit(momentItem)
}
item {
Spacer(modifier = Modifier.height(48.dp))
}
items(moments.itemCount) { idx ->
val momentItem = moments[idx] ?: return@items
MomentPostUnit(momentItem)
}
item {
Spacer(modifier = Modifier.height(48.dp))
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
@@ -541,7 +542,7 @@ fun CommunicationOperatorGroup(
contentDescription = ""
)
Text(
text = "Edit profile",
text = stringResource(R.string.edit_profile),
fontSize = 14.sp,
color = Color.Black,
fontWeight = FontWeight.Bold,
@@ -619,7 +620,7 @@ fun RidingStyleItem(styleContent: String) {
@Composable
fun EmptyMomentPostUnit() {
TimeGroup("You haven't left any tracks yet")
TimeGroup(stringResource(R.string.empty_my_post_title))
ProfileEmptyMomentCard()
}
@@ -660,7 +661,7 @@ fun ProfileEmptyMomentCard(
columnHeight = coordinates.size.height
}
) {
Text("Post a moment now", fontSize = 16.sp)
Text(stringResource(R.string.empty_my_post_content), fontSize = 16.sp)
Spacer(modifier = Modifier.height(24.dp))
Box(
modifier = Modifier

View File

@@ -11,6 +11,7 @@ import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.entity.MomentServiceImpl
import com.aiosman.riderpro.data.UploadImage
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.riderpro.ui.modification.Modification
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -75,6 +76,8 @@ object NewPostViewModel : ViewModel() {
index += 1
}
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
// 刷新个人动态
MyProfileViewModel.loadProfile()
}
suspend fun init() {

View File

@@ -45,4 +45,9 @@
<string name="error_unknown">服务端未知错误</string>
<string name="error_not_accept_recive_notice">为了为您提供更个性化的服务,请允许我们向您推送相关信息。</string>
<string name="error_not_accept_term">"为了提供更好的服务,请您在注册前仔细阅读并同意《用户协议》。 "</string>
<string name="empty_my_post_title">还没有发布任何动态</string>
<string name="empty_my_post_content">发布一个动态吧</string>
<string name="edit_profile">编辑个人资料</string>
<string name="logout">登出</string>
<string name="change_password">变更密码</string>
</resources>

View File

@@ -44,4 +44,9 @@
<string name="error_unknown">Unknown error</string>
<string name="error_not_accept_recive_notice">To provide you with a more personalized experience, please allow us to send you relevant notifications.</string>
<string name="error_not_accept_term">To provide you with the best service, please read and agree to our User Agreement before registering.</string>
<string name="empty_my_post_title">You haven\'t left any tracks yet</string>
<string name="empty_my_post_content">Post a moment now</string>
<string name="edit_profile">Edit profile</string>
<string name="logout">Logout</string>
<string name="change_password">Change password</string>
</resources>