修复多个界面和权限相关问题
- 修复其他用户个人主页群聊列表显示问题 - 移除不必要的全部、公开、私有标签栏 - 修复搜索框切换类型闪退 - 修复摄像机权限被拒绝时的闪退问题 - 修复视频动态显示不全问题 - 移除标签栏触摸反馈
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.aiosman.ravenow.entity
|
||||
|
||||
import android.util.Log
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.aiosman.ravenow.data.ListContainer
|
||||
@@ -272,19 +273,41 @@ class RoomRemoteDataSource {
|
||||
pageSize: Int = 20,
|
||||
search: String
|
||||
): ListContainer<RoomEntity>? {
|
||||
val resp = ApiClient.api.getRooms(
|
||||
page = pageNumber,
|
||||
pageSize = pageSize,
|
||||
search = search,
|
||||
roomType = "public" // 搜索时只显示公有房间
|
||||
)
|
||||
val body = resp.body() ?: return null
|
||||
return ListContainer(
|
||||
total = body.total,
|
||||
page = pageNumber,
|
||||
pageSize = pageSize,
|
||||
list = body.list.map { it.toRoomtEntity() }
|
||||
)
|
||||
return try {
|
||||
val resp = ApiClient.api.getRooms(
|
||||
page = pageNumber,
|
||||
pageSize = pageSize,
|
||||
search = search,
|
||||
roomType = "public" // 搜索时只显示公有房间
|
||||
)
|
||||
if (!resp.isSuccessful) {
|
||||
// API 调用失败,返回 null
|
||||
return null
|
||||
}
|
||||
val body = resp.body() ?: return null
|
||||
|
||||
// 安全地转换数据,过滤掉转换失败的项目
|
||||
val roomList = body.list.mapNotNull { room ->
|
||||
try {
|
||||
room.toRoomtEntity()
|
||||
} catch (e: Exception) {
|
||||
// 如果某个房间数据转换失败,记录错误但继续处理其他房间
|
||||
Log.e("RoomRemoteDataSource", "Failed to convert room: ${room.id}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
ListContainer(
|
||||
total = body.total,
|
||||
page = pageNumber,
|
||||
pageSize = pageSize,
|
||||
list = roomList
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
// 捕获所有异常,返回 null 让 PagingSource 处理
|
||||
Log.e("RoomRemoteDataSource", "searchRooms error", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,17 +326,31 @@ class RoomSearchPagingSource(
|
||||
pageSize = params.loadSize,
|
||||
search = keyword
|
||||
)
|
||||
LoadResult.Page(
|
||||
data = rooms?.list ?: listOf(),
|
||||
prevKey = if (currentPage == 1) null else currentPage - 1,
|
||||
nextKey = if (rooms?.list?.isNotEmpty() == true) currentPage + 1 else null
|
||||
)
|
||||
} catch (exception: IOException) {
|
||||
if (rooms == null) {
|
||||
// API 调用失败,返回空列表
|
||||
LoadResult.Page(
|
||||
data = emptyList(),
|
||||
prevKey = if (currentPage == 1) null else currentPage - 1,
|
||||
nextKey = null
|
||||
)
|
||||
} else {
|
||||
LoadResult.Page(
|
||||
data = rooms.list,
|
||||
prevKey = if (currentPage == 1) null else currentPage - 1,
|
||||
nextKey = if (rooms.list.isNotEmpty() && rooms.list.size >= params.loadSize) currentPage + 1 else null
|
||||
)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
// 捕获所有异常,包括 IOException、ServiceException 等
|
||||
LoadResult.Error(exception)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, RoomEntity>): Int? {
|
||||
return state.anchorPosition
|
||||
// 更健壮的实现:根据 anchorPosition 计算刷新键
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
|
||||
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ import com.aiosman.ravenow.event.MomentFavouriteChangeEvent
|
||||
import com.aiosman.ravenow.event.MomentLikeChangeEvent
|
||||
import com.aiosman.ravenow.event.MomentRemoveEvent
|
||||
import com.aiosman.ravenow.data.PointService
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@@ -308,8 +310,9 @@ object MyProfileViewModel : ViewModel() {
|
||||
* 加载房间列表
|
||||
* @param filterType 筛选类型:0=全部,1=公开,2=私有
|
||||
* @param pullRefresh 是否下拉刷新
|
||||
* @param ownerSessionId 创建者用户ID(ChatAIID),用于过滤特定创建者的房间。如果为null,则显示当前用户创建或加入的房间
|
||||
*/
|
||||
fun loadRooms(filterType: Int = 0, pullRefresh: Boolean = false) {
|
||||
fun loadRooms(filterType: Int = 0, pullRefresh: Boolean = false, ownerSessionId: String? = null) {
|
||||
// 游客模式下不加载房间列表
|
||||
if (AppStore.isGuest) {
|
||||
Log.d("MyProfileViewModel", "loadRooms: 游客模式下跳过加载房间列表")
|
||||
@@ -331,60 +334,114 @@ object MyProfileViewModel : ViewModel() {
|
||||
roomsCurrentPage
|
||||
}
|
||||
|
||||
val response = when (filterType) {
|
||||
0 -> {
|
||||
// 全部:显示自己创建或加入的所有房间
|
||||
apiClient.getRooms(
|
||||
page = currentPage,
|
||||
pageSize = roomsPageSize,
|
||||
showCreated = true,
|
||||
showJoined = true
|
||||
)
|
||||
}
|
||||
1 -> {
|
||||
// 公开:显示公开房间中自己创建或加入的
|
||||
apiClient.getRooms(
|
||||
page = currentPage,
|
||||
pageSize = roomsPageSize,
|
||||
roomType = "public",
|
||||
showCreated = true,
|
||||
showJoined = true
|
||||
)
|
||||
}
|
||||
2 -> {
|
||||
// 私有:显示自己创建或加入的私有房间
|
||||
apiClient.getRooms(
|
||||
page = currentPage,
|
||||
pageSize = roomsPageSize,
|
||||
roomType = "private"
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
apiClient.getRooms(
|
||||
page = currentPage,
|
||||
pageSize = roomsPageSize,
|
||||
showCreated = true,
|
||||
showJoined = true
|
||||
)
|
||||
}
|
||||
// 根据filterType确定roomType
|
||||
val roomType = when (filterType) {
|
||||
1 -> "public"
|
||||
2 -> "private"
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val roomList = response.body()?.list ?: emptyList()
|
||||
val total = response.body()?.total ?: 0L
|
||||
// 构建API调用参数
|
||||
if (ownerSessionId != null) {
|
||||
// 查看其他用户的房间:显示该用户创建和加入的房间
|
||||
// 1. 先快速获取该用户创建的房间(不需要 includeUsers,减少数据量)
|
||||
val createdResponse = apiClient.getRooms(
|
||||
page = currentPage,
|
||||
pageSize = roomsPageSize,
|
||||
roomType = roomType,
|
||||
ownerSessionId = ownerSessionId,
|
||||
includeUsers = false
|
||||
)
|
||||
|
||||
if (pullRefresh || currentPage == 1) {
|
||||
rooms = roomList.map { it.toRoomtEntity() }
|
||||
val createdRooms = if (createdResponse.isSuccessful) {
|
||||
createdResponse.body()?.list?.map { it.toRoomtEntity() } ?: emptyList()
|
||||
} else {
|
||||
rooms = rooms + roomList.map { it.toRoomtEntity() }
|
||||
emptyList()
|
||||
}
|
||||
|
||||
// 先快速显示创建的房间,提升用户体验
|
||||
if (pullRefresh || currentPage == 1) {
|
||||
rooms = createdRooms
|
||||
} else {
|
||||
rooms = rooms + createdRooms
|
||||
}
|
||||
|
||||
// 处理分页(基于创建的房间)
|
||||
val total = createdResponse.body()?.total ?: 0L
|
||||
roomsHasMore = rooms.size < total
|
||||
if (roomsHasMore && !pullRefresh) {
|
||||
roomsCurrentPage++
|
||||
}
|
||||
|
||||
// 2. 后台异步获取该用户加入的房间(仅在非私有房间时获取,且只在第一页时获取以提升性能)
|
||||
if (filterType != 2 && currentPage == 1) {
|
||||
launch {
|
||||
try {
|
||||
// 获取公开房间,但限制数量以减少数据量
|
||||
val joinedResponse = apiClient.getRooms(
|
||||
page = 1,
|
||||
pageSize = roomsPageSize,
|
||||
roomType = if (filterType == 1) "public" else null,
|
||||
includeUsers = true // 需要成员信息来判断
|
||||
)
|
||||
|
||||
if (joinedResponse.isSuccessful) {
|
||||
val joinedRooms = joinedResponse.body()?.list?.mapNotNull { room ->
|
||||
try {
|
||||
val entity = room.toRoomtEntity()
|
||||
// 检查房间的创建者是否是该用户
|
||||
val isCreatedByUser = entity.creator.profile.chatAIId == ownerSessionId
|
||||
// 检查房间的成员是否包含该用户
|
||||
val isMember = entity.users.any { it.profile.chatAIId == ownerSessionId }
|
||||
|
||||
// 如果用户是成员但不是创建者,则认为是加入的房间
|
||||
if (isMember && !isCreatedByUser) {
|
||||
entity
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("MyProfileViewModel", "处理房间失败: ${e.message}")
|
||||
null
|
||||
}
|
||||
} ?: emptyList()
|
||||
|
||||
// 合并并去重(基于房间ID),然后更新列表
|
||||
val allRooms = (rooms + joinedRooms).distinctBy { it.id }
|
||||
rooms = allRooms
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("MyProfileViewModel", "获取加入的房间失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e("MyProfileViewModel", "loadRooms failed: ${response.code()}")
|
||||
// 查看自己的房间:显示创建或加入的房间
|
||||
val response = apiClient.getRooms(
|
||||
page = currentPage,
|
||||
pageSize = roomsPageSize,
|
||||
roomType = roomType,
|
||||
showCreated = true,
|
||||
showJoined = if (filterType == 2) null else true // 私有房间不显示加入的
|
||||
)
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val roomList = response.body()?.list ?: emptyList()
|
||||
val total = response.body()?.total ?: 0L
|
||||
|
||||
if (pullRefresh || currentPage == 1) {
|
||||
rooms = roomList.map { it.toRoomtEntity() }
|
||||
} else {
|
||||
rooms = rooms + roomList.map { it.toRoomtEntity() }
|
||||
}
|
||||
|
||||
roomsHasMore = rooms.size < total
|
||||
if (roomsHasMore && !pullRefresh) {
|
||||
roomsCurrentPage++
|
||||
}
|
||||
} else {
|
||||
Log.e("MyProfileViewModel", "loadRooms failed: ${response.code()}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("MyProfileViewModel", "loadRooms error: ", e)
|
||||
@@ -398,20 +455,22 @@ object MyProfileViewModel : ViewModel() {
|
||||
/**
|
||||
* 加载更多房间
|
||||
* @param filterType 筛选类型:0=全部,1=公开,2=私有
|
||||
* @param ownerSessionId 创建者用户ID(ChatAIID),用于过滤特定创建者的房间
|
||||
*/
|
||||
fun loadMoreRooms(filterType: Int = 0) {
|
||||
fun loadMoreRooms(filterType: Int = 0, ownerSessionId: String? = null) {
|
||||
if (roomsLoading || !roomsHasMore) return
|
||||
loadRooms(filterType = filterType, pullRefresh = false)
|
||||
loadRooms(filterType = filterType, pullRefresh = false, ownerSessionId = ownerSessionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新房间列表
|
||||
* @param filterType 筛选类型:0=全部,1=公开,2=私有
|
||||
* @param ownerSessionId 创建者用户ID(ChatAIID),用于过滤特定创建者的房间
|
||||
*/
|
||||
fun refreshRooms(filterType: Int = 0) {
|
||||
fun refreshRooms(filterType: Int = 0, ownerSessionId: String? = null) {
|
||||
rooms = emptyList()
|
||||
roomsCurrentPage = 1
|
||||
roomsHasMore = true
|
||||
loadRooms(filterType = filterType, pullRefresh = true)
|
||||
loadRooms(filterType = filterType, pullRefresh = true, ownerSessionId = ownerSessionId)
|
||||
}
|
||||
}
|
||||
@@ -533,22 +533,29 @@ fun ProfileV3(
|
||||
showNoMoreText = isSelf,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
nestedScrollConnection = nestedScrollConnection
|
||||
nestedScrollConnection = nestedScrollConnection,
|
||||
showSegments = isSelf // 只有查看自己的主页时才显示分段控制器
|
||||
)
|
||||
} else {
|
||||
// 查看其他用户的主页时,传递该用户的chatAIId以显示其创建的群聊;查看自己的主页时传递null
|
||||
GroupChatPlaceholder(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
listState = groupChatListState,
|
||||
nestedScrollConnection = nestedScrollConnection
|
||||
nestedScrollConnection = nestedScrollConnection,
|
||||
ownerSessionId = if (!isSelf) profile?.chatAIId else null,
|
||||
showSegments = isSelf // 只有查看自己的主页时才显示分段控制器
|
||||
)
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
if (!isAiAccount) {
|
||||
// 查看其他用户的主页时,传递该用户的chatAIId以显示其创建的群聊;查看自己的主页时传递null
|
||||
GroupChatPlaceholder(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
listState = groupChatListState,
|
||||
nestedScrollConnection = nestedScrollConnection
|
||||
nestedScrollConnection = nestedScrollConnection,
|
||||
ownerSessionId = if (!isSelf) profile?.chatAIId else null,
|
||||
showSegments = isSelf // 只有查看自己的主页时才显示分段控制器
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -656,12 +663,16 @@ fun ProfileV3(
|
||||
private fun GroupChatPlaceholder(
|
||||
modifier: Modifier = Modifier,
|
||||
listState: androidx.compose.foundation.lazy.LazyListState,
|
||||
nestedScrollConnection: NestedScrollConnection? = null
|
||||
nestedScrollConnection: NestedScrollConnection? = null,
|
||||
ownerSessionId: String? = null, // 创建者用户ID(ChatAIID),用于过滤特定创建者的房间。如果为null,则显示当前用户创建或加入的房间
|
||||
showSegments: Boolean = true // 是否显示分段控制器(全部、公开、私有)
|
||||
) {
|
||||
GroupChatEmptyContent(
|
||||
modifier = modifier,
|
||||
listState = listState,
|
||||
nestedScrollConnection = nestedScrollConnection
|
||||
nestedScrollConnection = nestedScrollConnection,
|
||||
ownerSessionId = ownerSessionId,
|
||||
showSegments = showSegments
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.profile.composable
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.entity.MomentEntity
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.ravenow.ui.navigateToPost
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.entity.MomentEntity
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.ravenow.ui.navigateToPost
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.aiosman.ravenow.AppState
|
||||
@@ -227,8 +227,20 @@ fun GalleryGrid(
|
||||
.padding(bottom = 8.dp),
|
||||
) {
|
||||
itemsIndexed(moments) { idx, moment ->
|
||||
if (moment != null && moment.images.isNotEmpty()) {
|
||||
moment?.let { momentItem ->
|
||||
val itemDebouncer = rememberDebouncer()
|
||||
val isVideoMoment = momentItem.images.isEmpty() && !momentItem.videos.isNullOrEmpty()
|
||||
val previewUrl = when {
|
||||
momentItem.images.isNotEmpty() -> momentItem.images[0].thumbnail
|
||||
isVideoMoment -> {
|
||||
val firstVideo = momentItem.videos!!.first()
|
||||
firstVideo.thumbnailDirectUrl
|
||||
?: firstVideo.thumbnailUrl
|
||||
?: firstVideo.directUrl
|
||||
?: firstVideo.url
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -237,31 +249,29 @@ fun GalleryGrid(
|
||||
.noRippleClickable {
|
||||
itemDebouncer {
|
||||
navController.navigateToPost(
|
||||
id = moment.id,
|
||||
id = momentItem.id,
|
||||
highlightCommentId = 0,
|
||||
initImagePagerIndex = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
imageUrl = moment.images[0].thumbnail,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
context = LocalContext.current
|
||||
)
|
||||
if (moment.images.size > 1) {
|
||||
if (previewUrl != null) {
|
||||
CustomAsyncImage(
|
||||
imageUrl = previewUrl,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
context = LocalContext.current
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, end = 8.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.size(24.dp),
|
||||
painter = painterResource(R.drawable.rider_pro_picture_more),
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
color = AppColors.basicMain.copy(alpha = 0.2f),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,9 @@ import android.util.Base64
|
||||
fun GroupChatEmptyContent(
|
||||
modifier: Modifier = Modifier,
|
||||
listState: LazyListState,
|
||||
nestedScrollConnection: NestedScrollConnection? = null
|
||||
nestedScrollConnection: NestedScrollConnection? = null,
|
||||
ownerSessionId: String? = null, // 创建者用户ID(ChatAIID),用于过滤特定创建者的房间。如果为null,则显示当前用户创建或加入的房间
|
||||
showSegments: Boolean = true // 是否显示分段控制器(全部、公开、私有)
|
||||
) {
|
||||
var selectedSegment by remember { mutableStateOf(0) } // 0: 全部, 1: 公开, 2: 私有
|
||||
val AppColors = LocalAppTheme.current
|
||||
@@ -77,24 +79,19 @@ fun GroupChatEmptyContent(
|
||||
val navController = LocalNavController.current
|
||||
val viewModel = MyProfileViewModel
|
||||
|
||||
// 如果查看其他用户的房间,固定使用全部类型(filterType = 0)
|
||||
val filterType = if (showSegments) selectedSegment else 0
|
||||
|
||||
val state = rememberPullRefreshState(
|
||||
refreshing = viewModel.roomsRefreshing,
|
||||
onRefresh = {
|
||||
viewModel.refreshRooms(filterType = selectedSegment)
|
||||
viewModel.refreshRooms(filterType = filterType, ownerSessionId = ownerSessionId)
|
||||
}
|
||||
)
|
||||
|
||||
// 当分段改变时,重新加载数据
|
||||
LaunchedEffect(selectedSegment) {
|
||||
// 切换分段时重新加载
|
||||
viewModel.refreshRooms(filterType = selectedSegment)
|
||||
}
|
||||
|
||||
// 初始加载
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.rooms.isEmpty() && !viewModel.roomsLoading) {
|
||||
viewModel.loadRooms(filterType = selectedSegment)
|
||||
}
|
||||
// 当分段或用户ID改变时,重新加载数据
|
||||
LaunchedEffect(selectedSegment, ownerSessionId, showSegments) {
|
||||
viewModel.refreshRooms(filterType = filterType, ownerSessionId = ownerSessionId)
|
||||
}
|
||||
|
||||
val nestedScrollModifier = if (nestedScrollConnection != null) {
|
||||
@@ -110,17 +107,19 @@ fun GroupChatEmptyContent(
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 分段控制器
|
||||
SegmentedControl(
|
||||
selectedIndex = selectedSegment,
|
||||
onSegmentSelected = {
|
||||
selectedSegment = it
|
||||
// LaunchedEffect 会监听 selectedSegment 的变化并自动刷新
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
// 只在查看自己的房间时显示分段控制器
|
||||
if (showSegments) {
|
||||
SegmentedControl(
|
||||
selectedIndex = selectedSegment,
|
||||
onSegmentSelected = {
|
||||
selectedSegment = it
|
||||
// LaunchedEffect 会监听 selectedSegment 的变化并自动刷新
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = nestedScrollModifier
|
||||
@@ -201,7 +200,7 @@ fun GroupChatEmptyContent(
|
||||
if (viewModel.roomsHasMore && !viewModel.roomsLoading) {
|
||||
item {
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.loadMoreRooms(filterType = selectedSegment)
|
||||
viewModel.loadMoreRooms(filterType = filterType, ownerSessionId = ownerSessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,7 +371,7 @@ private fun SegmentButton(
|
||||
},
|
||||
shape = RoundedCornerShape(1000.dp)
|
||||
)
|
||||
.clickable(onClick = onClick),
|
||||
.noRippleClickable { onClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -66,7 +67,8 @@ fun UserAgentsList(
|
||||
showNoMoreText: Boolean = false,
|
||||
modifier: Modifier = Modifier,
|
||||
state: LazyListState,
|
||||
nestedScrollConnection: NestedScrollConnection? = null
|
||||
nestedScrollConnection: NestedScrollConnection? = null,
|
||||
showSegments: Boolean = true // 是否显示分段控制器(全部、公开、私有)
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val listModifier = if (nestedScrollConnection != null) {
|
||||
@@ -80,7 +82,7 @@ fun UserAgentsList(
|
||||
Box(
|
||||
modifier = listModifier.fillMaxSize()
|
||||
) {
|
||||
AgentEmptyContentWithSegments()
|
||||
AgentEmptyContentWithSegments(showSegments = showSegments)
|
||||
}
|
||||
} else {
|
||||
LazyColumn(
|
||||
@@ -248,7 +250,9 @@ fun UserAgentCard(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AgentEmptyContentWithSegments() {
|
||||
fun AgentEmptyContentWithSegments(
|
||||
showSegments: Boolean = true // 是否显示分段控制器(全部、公开、私有)
|
||||
) {
|
||||
var selectedSegment by remember { mutableStateOf(0) } // 0: 全部, 1: 公开, 2: 私有
|
||||
val AppColors = LocalAppTheme.current
|
||||
val isNetworkAvailable = NetworkUtils.isNetworkAvailable(LocalContext.current)
|
||||
@@ -260,14 +264,16 @@ fun AgentEmptyContentWithSegments() {
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 分段控制器
|
||||
AgentSegmentedControl(
|
||||
selectedIndex = selectedSegment,
|
||||
onSegmentSelected = { selectedSegment = it },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
// 只在查看自己的智能体时显示分段控制器
|
||||
if (showSegments) {
|
||||
AgentSegmentedControl(
|
||||
selectedIndex = selectedSegment,
|
||||
onSegmentSelected = { selectedSegment = it },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
// 空状态内容(与动态、群聊保持一致)
|
||||
Column(
|
||||
@@ -397,7 +403,7 @@ private fun AgentSegmentButton(
|
||||
},
|
||||
shape = RoundedCornerShape(1000.dp)
|
||||
)
|
||||
.clickable(onClick = onClick),
|
||||
.noRippleClickable { onClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.aiosman.ravenow.ui.post
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
@@ -588,6 +591,34 @@ fun AddImageGrid() {
|
||||
}
|
||||
}
|
||||
|
||||
// 摄像头权限请求
|
||||
val requestCameraPermissionLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.RequestPermission()
|
||||
) { isGranted ->
|
||||
if (isGranted) {
|
||||
// 权限已授予,打开相机
|
||||
if (model.imageList.size < 9) {
|
||||
val photoFile = File(context.cacheDir, "photo.jpg")
|
||||
val photoUri: Uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
photoFile
|
||||
)
|
||||
model.currentPhotoUri = photoUri
|
||||
takePictureLauncher.launch(photoUri)
|
||||
} else {
|
||||
Toast.makeText(context, "最多只能选择9张图片", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
// 权限被拒绝,提示用户
|
||||
Toast.makeText(
|
||||
context,
|
||||
"需要摄像头权限才能拍摄照片,请在设置中开启",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
val addImageDebouncer = rememberDebouncer()
|
||||
val canAddMoreImages = model.imageList.size < 9
|
||||
|
||||
@@ -642,14 +673,27 @@ fun AddImageGrid() {
|
||||
.background(Color(0xFFFAF9FB))
|
||||
.noRippleClickable {
|
||||
if (model.imageList.size < 9) {
|
||||
val photoFile = File(context.cacheDir, "photo.jpg")
|
||||
val photoUri: Uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
photoFile
|
||||
)
|
||||
model.currentPhotoUri = photoUri
|
||||
takePictureLauncher.launch(photoUri)
|
||||
// 检查摄像头权限
|
||||
when {
|
||||
ContextCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.CAMERA
|
||||
) == PackageManager.PERMISSION_GRANTED -> {
|
||||
// 已有权限,直接打开相机
|
||||
val photoFile = File(context.cacheDir, "photo.jpg")
|
||||
val photoUri: Uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
photoFile
|
||||
)
|
||||
model.currentPhotoUri = photoUri
|
||||
takePictureLauncher.launch(photoUri)
|
||||
}
|
||||
else -> {
|
||||
// 没有权限,请求权限
|
||||
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, "最多只能选择9张图片", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user