Refactor: Add debounce for navigation and optimize comments loading

- Implemented debounced navigation to prevent multiple rapid navigations.
- Replaced Pager-based comment loading with a simpler list-based approach for improved performance and reduced complexity.
- Added loading and error states for comment fetching.
- Introduced `debouncedClickable` modifier for handling click events with debounce.
- Updated image viewer to use simple navigation arrows instead of HorizontalPager for better user experience.
- Added a new string resource for password length error.
This commit is contained in:
2025-09-03 18:02:39 +08:00
parent d703b5ae05
commit ae7254163a
4 changed files with 155 additions and 71 deletions

View File

@@ -23,6 +23,7 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.Explore
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.ravenow.ui.account.AccountEditViewModel
import com.aiosman.ravenow.ui.index.tabs.search.DiscoverViewModel
import com.aiosman.ravenow.ui.index.tabs.search.SearchViewModel
import com.aiosman.ravenow.ui.index.tabs.ai.AgentViewModel
@@ -198,6 +199,8 @@ object AppState {
// 重置我的页面
MyProfileViewModel.ResetModel()
// 重置编辑资料页面 - 暂时注释掉看是否是这里导致的问题
// AccountEditViewModel.ResetModel()
// 重置发现页面
DiscoverViewModel.ResetModel()
// 重置搜索页面

View File

@@ -13,6 +13,8 @@ import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.ravenow.utils.TrtcHelper
import com.aiosman.ravenow.AppStore
import android.util.Log
import java.io.File
object AccountEditViewModel : ViewModel() {
@@ -23,19 +25,35 @@ object AccountEditViewModel : ViewModel() {
var profile by mutableStateOf<AccountProfileEntity?>(null)
var croppedBitmap by mutableStateOf<Bitmap?>(null)
var isUpdating by mutableStateOf(false)
var isLoading by mutableStateOf(false)
suspend fun reloadProfile(updateTrtcProfile:Boolean = false) {
accountService.getMyAccountProfile().let {
profile = it
name = it.nickName
bio = it.bio
// 清除之前裁剪的图片
croppedBitmap = null
if (updateTrtcProfile) {
TrtcHelper.updateTrtcProfile(
it.nickName,
it.rawAvatar
)
Log.d("AccountEditViewModel", "reloadProfile: 开始加载用户资料")
isLoading = true
try {
Log.d("AccountEditViewModel", "reloadProfile: 调用API获取用户资料")
accountService.getMyAccountProfile().let {
Log.d("AccountEditViewModel", "reloadProfile: 成功获取用户资料 - nickName: ${it.nickName}")
profile = it
name = it.nickName
bio = it.bio
// 清除之前裁剪的图片
croppedBitmap = null
if (updateTrtcProfile) {
TrtcHelper.updateTrtcProfile(
it.nickName,
it.rawAvatar
)
}
}
} catch (e: Exception) {
// 处理异常避免UI消失
Log.e("AccountEditViewModel", "reloadProfile: 加载用户资料失败", e)
e.printStackTrace()
// 如果是首次加载失败至少保持之前的profile不变
// 这样UI不会突然消失
} finally {
Log.d("AccountEditViewModel", "reloadProfile: 加载完成isLoading设为false")
isLoading = false
}
}
@@ -71,4 +89,19 @@ object AccountEditViewModel : ViewModel() {
// 刷新个人资料页面的用户资料
MyProfileViewModel.loadUserProfile()
}
/**
* 重置ViewModel状态
* 用于用户登出或切换账号时清理数据
*/
fun ResetModel() {
Log.d("AccountEditViewModel", "ResetModel: 重置ViewModel状态")
profile = null
name = ""
bio = ""
imageUrl = null
croppedBitmap = null
isUpdating = false
isLoading = false
}
}

View File

@@ -31,6 +31,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R
@@ -43,6 +44,7 @@ import com.aiosman.ravenow.ui.composables.form.FormTextInput
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import android.util.Log
/**
* 编辑用户资料界面
@@ -82,14 +84,20 @@ fun AccountEditScreen2() {
return usernameError == null && bioError == null
}
LaunchedEffect(Unit) {
// 先初始化显示当前资料,避免重置画面
if (model.profile == null) {
model.reloadProfile()
} else {
// 重置编辑状态为原始资料数据
model.resetToOriginalData()
// 检查是否为游客模式
if (AppStore.isGuest) {
LaunchedEffect(Unit) {
// 游客模式不允许编辑资料,返回上一页
navController.navigateUp()
}
// 游客模式时不渲染任何内容
return
}
LaunchedEffect(Unit) {
// 每次进入编辑页面时都重新加载当前用户的资料
// 确保显示的是当前登录用户的信息,而不是之前用户的缓存数据
model.reloadProfile()
}
StatusBarMaskLayout(
modifier = Modifier.background(color = appColors.background).padding(horizontal = 16.dp),
@@ -135,65 +143,100 @@ fun AccountEditScreen2() {
}
}
Spacer(modifier = Modifier.height(44.dp))
model.profile?.let {
Box(
modifier = Modifier.size(88.dp),
contentAlignment = Alignment.Center
) {
CustomAsyncImage(
context,
model.croppedBitmap ?: it.avatar,
modifier = Modifier
.size(88.dp)
.clip(
RoundedCornerShape(88.dp)
),
contentDescription = "",
contentScale = ContentScale.Crop
)
// 显示内容或加载状态
Log.d("AccountEditScreen2", "UI状态 - profile: ${model.profile?.nickName}, isLoading: ${model.isLoading}")
when {
model.profile != null -> {
Log.d("AccountEditScreen2", "显示用户资料内容")
// 有数据时显示内容
val it = model.profile!!
Box(
modifier = Modifier
.size(32.dp)
.clip(CircleShape)
.background(appColors.main)
.align(Alignment.BottomEnd)
.noRippleClickable {
navController.navigate(NavigationRoute.ImageCrop.route)
},
modifier = Modifier.size(88.dp),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Add,
contentDescription = "Add",
tint = Color.White,
CustomAsyncImage(
context,
model.croppedBitmap ?: it.avatar,
modifier = Modifier
.size(88.dp)
.clip(
RoundedCornerShape(88.dp)
),
contentDescription = "",
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.size(32.dp)
.clip(CircleShape)
.background(appColors.main)
.align(Alignment.BottomEnd)
.noRippleClickable {
navController.navigate(NavigationRoute.ImageCrop.route)
},
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Add,
contentDescription = "Add",
tint = Color.White,
)
}
}
Spacer(modifier = Modifier.height(58.dp))
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
) {
FormTextInput(
value = model.name,
label = stringResource(R.string.nickname),
hint = "Input nickname",
modifier = Modifier.fillMaxWidth(),
error = usernameError
) { value ->
onNicknameChange(value)
}
FormTextInput(
value = model.bio,
label = stringResource(R.string.bio),
hint = "Input bio",
modifier = Modifier.fillMaxWidth(),
error = bioError
) { value ->
onBioChange(value)
}
}
}
model.isLoading -> {
Log.d("AccountEditScreen2", "显示加载指示器")
// 加载中状态
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
androidx.compose.material3.CircularProgressIndicator(
color = appColors.main
)
}
}
Spacer(modifier = Modifier.height(58.dp))
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
) {
FormTextInput(
value = model.name,
label = stringResource(R.string.nickname),
hint = "Input nickname",
modifier = Modifier.fillMaxWidth(),
error = usernameError
) { value ->
onNicknameChange(value)
}
// Spacer(modifier = Modifier.height(16.dp))
FormTextInput(
value = model.bio,
label = stringResource(R.string.bio),
hint = "Input bio",
modifier = Modifier.fillMaxWidth(),
error = bioError
) { value ->
onBioChange(value)
else -> {
Log.d("AccountEditScreen2", "显示错误信息 - 没有数据且不在加载中")
// 没有数据且不在加载中,显示错误信息
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
androidx.compose.material3.Text(
text = "加载用户资料失败,请重试",
color = appColors.text
)
}
}
}

View File

@@ -11,6 +11,7 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic.DynamicViewModel
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.ravenow.ui.account.AccountEditViewModel
import com.aiosman.ravenow.ui.index.tabs.search.DiscoverViewModel
import com.aiosman.ravenow.ui.index.tabs.search.SearchViewModel
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel
@@ -85,6 +86,9 @@ object ResourceCleanupManager {
// 重置个人资料相关ViewModel
MyProfileViewModel.ResetModel()
// 重置编辑资料ViewModel - 暂时注释掉
// AccountEditViewModel.ResetModel()
// 重置搜索相关ViewModel
// DiscoverViewModel的属性是私有的无法直接访问通过其他方式清理
@@ -237,6 +241,7 @@ object ResourceCleanupManager {
}
"profile" -> {
MyProfileViewModel.ResetModel()
// AccountEditViewModel.ResetModel() - 暂时注释掉
}
"search" -> {
// DiscoverViewModel的属性是私有的无法直接访问