@@ -84,6 +84,8 @@ fun AddGroupMemberScreen(groupId: String, groupName: String?) {
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
systemUiController.setNavigationBarColor(Color.Transparent)
|
systemUiController.setNavigationBarColor(Color.Transparent)
|
||||||
AddGroupMemberViewModel.groupName = groupName
|
AddGroupMemberViewModel.groupName = groupName
|
||||||
|
AddGroupMemberViewModel.trtcId = groupId
|
||||||
|
AddGroupMemberViewModel.roomId = null
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.aiosman.ravenow.AppStore
|
import com.aiosman.ravenow.AppStore
|
||||||
import com.aiosman.ravenow.data.AccountService
|
import com.aiosman.ravenow.data.AccountService
|
||||||
import com.aiosman.ravenow.data.AccountServiceImpl
|
import com.aiosman.ravenow.data.AccountServiceImpl
|
||||||
|
import com.aiosman.ravenow.data.RoomService
|
||||||
|
import com.aiosman.ravenow.data.RoomServiceImpl
|
||||||
import com.aiosman.ravenow.data.UserService
|
import com.aiosman.ravenow.data.UserService
|
||||||
import com.aiosman.ravenow.data.UserServiceImpl
|
import com.aiosman.ravenow.data.UserServiceImpl
|
||||||
import com.aiosman.ravenow.data.api.ApiClient
|
import com.aiosman.ravenow.data.api.ApiClient
|
||||||
@@ -17,11 +19,14 @@ import kotlinx.coroutines.launch
|
|||||||
object AddGroupMemberViewModel : ViewModel() {
|
object AddGroupMemberViewModel : ViewModel() {
|
||||||
val accountService: AccountService = AccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
val userService: UserService = UserServiceImpl()
|
val userService: UserService = UserServiceImpl()
|
||||||
|
val roomService: RoomService = RoomServiceImpl()
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
var isLoading by mutableStateOf(false)
|
var isLoading by mutableStateOf(false)
|
||||||
var errorMessage by mutableStateOf<String?>(null)
|
var errorMessage by mutableStateOf<String?>(null)
|
||||||
var groupName: String? = null
|
var groupName: String? = null
|
||||||
|
var trtcId: String? = null
|
||||||
|
var roomId: Int? = null
|
||||||
|
|
||||||
// 添加成员到群聊
|
// 添加成员到群聊
|
||||||
suspend fun addMembersToGroup(
|
suspend fun addMembersToGroup(
|
||||||
@@ -30,26 +35,84 @@ object AddGroupMemberViewModel : ViewModel() {
|
|||||||
return try {
|
return try {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
if (groupName == null) {
|
// 验证房间标识
|
||||||
|
if (trtcId == null && roomId == null) {
|
||||||
isLoading = false
|
isLoading = false
|
||||||
val errorMsg = "群聊名称不能为空"
|
val errorMsg = "房间标识不能为空"
|
||||||
showToast(errorMsg)
|
showToast(errorMsg)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据isAi属性分别获取userIds和promptIds
|
// 根据isAi属性分别获取用户和智能体的OpenID列表
|
||||||
val userIds = selectedMembers.filter { !it.isAi }.map { it.id }
|
val userOpenIds = selectedMembers.filter { !it.isAi }.map { it.id }
|
||||||
val promptIds = selectedMembers.filter { it.isAi }.map { it.id }
|
val agentOpenIds = selectedMembers.filter { it.isAi }.map { it.id }
|
||||||
|
|
||||||
// 使用创建群聊的API,传入群聊名称来添加成员到现有群聊
|
var allSuccess = true
|
||||||
// 与创建群聊使用相同的方法,通过群聊名称标识目标群聊
|
var errorMessages = mutableListOf<String>()
|
||||||
val response = accountService.createGroupChat(groupName!!, userIds, promptIds, roomId = null)
|
|
||||||
if (response.isSuccessful && response.body() != null) {
|
// 添加用户到房间
|
||||||
isLoading = false
|
if (userOpenIds.isNotEmpty()) {
|
||||||
|
try {
|
||||||
|
val userResult = roomService.addUserToRoom(
|
||||||
|
roomId = roomId,
|
||||||
|
trtcId = trtcId,
|
||||||
|
openIds = userOpenIds
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查添加结果
|
||||||
|
if (userResult.failedCount > 0) {
|
||||||
|
val failedUsers = userResult.failedItems.joinToString(", ") { it.userId }
|
||||||
|
errorMessages.add("部分用户添加失败: $failedUsers")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userResult.successCount == 0 && userResult.failedCount > 0) {
|
||||||
|
allSuccess = false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
allSuccess = false
|
||||||
|
errorMessages.add("添加用户失败: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加智能体到房间
|
||||||
|
if (agentOpenIds.isNotEmpty()) {
|
||||||
|
try {
|
||||||
|
val agentResult = roomService.addAgentToRoom(
|
||||||
|
roomId = roomId,
|
||||||
|
trtcId = trtcId,
|
||||||
|
agentOpenIds = agentOpenIds
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查添加结果
|
||||||
|
if (agentResult.failedCount > 0) {
|
||||||
|
val failedAgents = agentResult.failedItems.joinToString(", ") { it.agentOpenId }
|
||||||
|
errorMessages.add("部分智能体添加失败: $failedAgents")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agentResult.successCount == 0 && agentResult.failedCount > 0) {
|
||||||
|
allSuccess = false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
allSuccess = false
|
||||||
|
errorMessages.add("添加智能体失败: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading = false
|
||||||
|
|
||||||
|
if (allSuccess) {
|
||||||
|
if (errorMessages.isNotEmpty()) {
|
||||||
|
// 有部分失败,但至少有一些成功
|
||||||
|
showToast(errorMessages.joinToString("\n"))
|
||||||
|
}
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
isLoading = false
|
// 全部失败
|
||||||
val errorMsg = "添加成员失败: ${response.message()}"
|
val errorMsg = if (errorMessages.isNotEmpty()) {
|
||||||
|
errorMessages.joinToString("\n")
|
||||||
|
} else {
|
||||||
|
"添加成员失败"
|
||||||
|
}
|
||||||
showToast(errorMsg)
|
showToast(errorMsg)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -62,11 +125,7 @@ object AddGroupMemberViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showToast(message: String) {
|
private fun showToast(message: String) {
|
||||||
errorMessage = message
|
Log.w("AddGroupMemberViewModel", message)
|
||||||
viewModelScope.launch {
|
|
||||||
kotlinx.coroutines.delay(3000)
|
|
||||||
errorMessage = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除错误信息
|
// 清除错误信息
|
||||||
|
|||||||
@@ -113,10 +113,5 @@ class GroupMembersViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleRequireApproval() {
|
|
||||||
requireApproval = !requireApproval
|
|
||||||
// TODO: 实现更新设置的API调用
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -323,84 +323,87 @@ fun Agent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item { Spacer(modifier = Modifier.height(20.dp)) }
|
// 只有当热门聊天室有数据时,才展示“发现更多”区域
|
||||||
|
if (viewModel.chatRooms.isNotEmpty()) {
|
||||||
|
item { Spacer(modifier = Modifier.height(20.dp)) }
|
||||||
|
|
||||||
// "发现更多" 标题 - 吸顶
|
// "发现更多" 标题 - 吸顶
|
||||||
stickyHeader(key = "discover_more") {
|
stickyHeader(key = "discover_more") {
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(AppColors.background)
|
|
||||||
.padding(top = 8.dp, bottom = 12.dp),
|
|
||||||
horizontalArrangement = Arrangement.Start,
|
|
||||||
verticalAlignment = Alignment.Bottom
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(R.mipmap.bars_x_buttons_home_n_copy_2),
|
|
||||||
contentDescription = "agent",
|
|
||||||
modifier = Modifier.size(28.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
androidx.compose.material3.Text(
|
|
||||||
text = stringResource(R.string.agent_find),
|
|
||||||
fontSize = 16.sp,
|
|
||||||
fontWeight = androidx.compose.ui.text.font.FontWeight.W900,
|
|
||||||
color = AppColors.text
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent网格 - 使用行式布局
|
|
||||||
items(
|
|
||||||
items = agentItems.chunked(2),
|
|
||||||
key = { row -> row.firstOrNull()?.openId ?: "" }
|
|
||||||
) { rowItems ->
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 16.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
|
||||||
rowItems.forEach { agentItem ->
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
AgentCardSquare(
|
|
||||||
agentItem = agentItem,
|
|
||||||
viewModel = viewModel,
|
|
||||||
navController = LocalNavController.current
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果这一行只有一个item,添加一个空的占位符
|
|
||||||
if (rowItems.size == 1) {
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载更多指示器
|
|
||||||
if (viewModel.isLoadingMore) {
|
|
||||||
item {
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 24.dp),
|
.background(AppColors.background)
|
||||||
horizontalArrangement = Arrangement.Center
|
.padding(top = 8.dp, bottom = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
verticalAlignment = Alignment.Bottom
|
||||||
) {
|
) {
|
||||||
androidx.compose.material3.CircularProgressIndicator(
|
Image(
|
||||||
modifier = Modifier.size(24.dp),
|
painter = painterResource(R.mipmap.bars_x_buttons_home_n_copy_2),
|
||||||
color = AppColors.text,
|
contentDescription = "agent",
|
||||||
strokeWidth = 2.dp
|
modifier = Modifier.size(28.dp)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
androidx.compose.material3.Text(
|
androidx.compose.material3.Text(
|
||||||
text = "加载中...",
|
text = stringResource(R.string.agent_find),
|
||||||
color = AppColors.secondaryText,
|
fontSize = 16.sp,
|
||||||
fontSize = 14.sp
|
fontWeight = androidx.compose.ui.text.font.FontWeight.W900,
|
||||||
|
color = AppColors.text
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Agent网格 - 使用行式布局
|
||||||
|
items(
|
||||||
|
items = agentItems.chunked(2),
|
||||||
|
key = { row -> row.firstOrNull()?.openId ?: "" }
|
||||||
|
) { rowItems ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
rowItems.forEach { agentItem ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
AgentCardSquare(
|
||||||
|
agentItem = agentItem,
|
||||||
|
viewModel = viewModel,
|
||||||
|
navController = LocalNavController.current
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果这一行只有一个item,添加一个空的占位符
|
||||||
|
if (rowItems.size == 1) {
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多指示器(仅在展示“发现更多”时显示)
|
||||||
|
if (viewModel.isLoadingMore) {
|
||||||
|
item {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 24.dp),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = AppColors.text,
|
||||||
|
strokeWidth = 2.dp
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
androidx.compose.material3.Text(
|
||||||
|
text = "加载中...",
|
||||||
|
color = AppColors.secondaryText,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ fun NotificationsScreen() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(24.dp)
|
.size(24.dp)
|
||||||
.noRippleClickable {
|
.noRippleClickable {
|
||||||
// TODO: 实现搜索功能
|
navController.navigate(NavigationRoute.Search.route)
|
||||||
},
|
},
|
||||||
colorFilter = ColorFilter.tint(AppColors.text)
|
colorFilter = ColorFilter.tint(AppColors.text)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,9 +29,12 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
@@ -57,6 +60,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.layout.positionInParent
|
||||||
import com.aiosman.ravenow.ui.composables.TabItem
|
import com.aiosman.ravenow.ui.composables.TabItem
|
||||||
import com.aiosman.ravenow.ui.composables.UnderlineTabItem
|
import com.aiosman.ravenow.ui.composables.UnderlineTabItem
|
||||||
import com.aiosman.ravenow.ui.composables.rememberDebouncer
|
import com.aiosman.ravenow.ui.composables.rememberDebouncer
|
||||||
@@ -77,6 +81,14 @@ fun MomentsList() {
|
|||||||
val tabCount = if (AppStore.isGuest) 5 else 6
|
val tabCount = if (AppStore.isGuest) 5 else 6
|
||||||
var pagerState = rememberPagerState { tabCount }
|
var pagerState = rememberPagerState { tabCount }
|
||||||
var scope = rememberCoroutineScope()
|
var scope = rememberCoroutineScope()
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
val density = LocalDensity.current
|
||||||
|
// 存储每个标签的位置和宽度信息 (页面索引, 位置, 宽度)
|
||||||
|
val tabPositions = remember { mutableStateOf<List<Triple<Int, Float, Float>>>(emptyList()) }
|
||||||
|
// 存储容器宽度
|
||||||
|
val containerWidth = remember { mutableStateOf(0f) }
|
||||||
|
// 记录上一次的页面索引
|
||||||
|
val previousPage = remember { mutableStateOf(pagerState.currentPage) }
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -96,113 +108,257 @@ fun MomentsList() {
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// 可滚动的标签页行
|
// 可滚动的标签页行
|
||||||
Row(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.horizontalScroll(rememberScrollState()),
|
.onGloballyPositioned { coordinates ->
|
||||||
horizontalArrangement = Arrangement.Start,
|
containerWidth.value = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
verticalAlignment = Alignment.CenterVertically
|
}
|
||||||
) {
|
) {
|
||||||
val tabDebouncer = rememberDebouncer()
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.horizontalScroll(scrollState),
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
val tabDebouncer = rememberDebouncer()
|
||||||
|
|
||||||
// 推荐标签
|
// 推荐标签
|
||||||
UnderlineTabItem(
|
Box(
|
||||||
text = stringResource(R.string.tab_recommend),
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
isSelected = pagerState.currentPage == 0,
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
onClick = {
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
tabDebouncer {
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
scope.launch {
|
val existingIndex = currentPositions.indexOfFirst { it.first == 0 }
|
||||||
pagerState.animateScrollToPage(0)
|
if (existingIndex == -1) {
|
||||||
|
currentPositions.add(Triple(0, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(0, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
) {
|
||||||
)
|
UnderlineTabItem(
|
||||||
|
text = stringResource(R.string.tab_recommend),
|
||||||
|
isSelected = pagerState.currentPage == 0,
|
||||||
// 短视频标签
|
onClick = {
|
||||||
UnderlineTabItem(
|
tabDebouncer {
|
||||||
text = stringResource(R.string.tab_short_video),
|
scope.launch {
|
||||||
isSelected = pagerState.currentPage == 1,
|
pagerState.animateScrollToPage(0)
|
||||||
onClick = {
|
}
|
||||||
tabDebouncer {
|
|
||||||
scope.launch {
|
|
||||||
pagerState.animateScrollToPage(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// 动态标签
|
|
||||||
UnderlineTabItem(
|
|
||||||
text = stringResource(R.string.moment),
|
|
||||||
isSelected = pagerState.currentPage == 2,
|
|
||||||
onClick = {
|
|
||||||
tabDebouncer {
|
|
||||||
scope.launch {
|
|
||||||
pagerState.animateScrollToPage(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// 只有非游客用户才显示"关注"tab
|
|
||||||
if (!AppStore.isGuest) {
|
|
||||||
UnderlineTabItem(
|
|
||||||
text = stringResource(R.string.index_following),
|
|
||||||
isSelected = pagerState.currentPage == 3,
|
|
||||||
onClick = {
|
|
||||||
tabDebouncer {
|
|
||||||
scope.launch {
|
|
||||||
pagerState.animateScrollToPage(3)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
}
|
||||||
|
|
||||||
|
|
||||||
// 热门标签
|
// 短视频标签
|
||||||
UnderlineTabItem(
|
Box(
|
||||||
text = stringResource(R.string.index_hot),
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
isSelected = pagerState.currentPage == 4,
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
onClick = {
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
tabDebouncer {
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
scope.launch {
|
val existingIndex = currentPositions.indexOfFirst { it.first == 1 }
|
||||||
pagerState.animateScrollToPage(4)
|
if (existingIndex == -1) {
|
||||||
}
|
currentPositions.add(Triple(1, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(1, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
) {
|
||||||
} else {
|
UnderlineTabItem(
|
||||||
// 热门标签 (游客模式) - 在游客模式下,热门标签对应第3页
|
text = stringResource(R.string.tab_short_video),
|
||||||
UnderlineTabItem(
|
isSelected = pagerState.currentPage == 1,
|
||||||
text = stringResource(R.string.index_hot),
|
onClick = {
|
||||||
isSelected = pagerState.currentPage == 3,
|
tabDebouncer {
|
||||||
onClick = {
|
scope.launch {
|
||||||
tabDebouncer {
|
pagerState.animateScrollToPage(1)
|
||||||
scope.launch {
|
}
|
||||||
pagerState.animateScrollToPage(3)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 动态标签
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
|
val existingIndex = currentPositions.indexOfFirst { it.first == 2 }
|
||||||
|
if (existingIndex == -1) {
|
||||||
|
currentPositions.add(Triple(2, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(2, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
) {
|
||||||
|
UnderlineTabItem(
|
||||||
|
text = stringResource(R.string.moment),
|
||||||
|
isSelected = pagerState.currentPage == 2,
|
||||||
|
onClick = {
|
||||||
|
tabDebouncer {
|
||||||
|
scope.launch {
|
||||||
|
// 如果当前在动态标签右边的标签页,立即向左滚动显示推荐标签
|
||||||
|
if (pagerState.currentPage > 2 && containerWidth.value > 0) {
|
||||||
|
val recommendTab = tabPositions.value.find { it.first == 0 }
|
||||||
|
if (recommendTab != null) {
|
||||||
|
val (_, recommendPosition, _) = recommendTab
|
||||||
|
val currentScroll = scrollState.value.toFloat()
|
||||||
|
val scrollToPosition = recommendPosition.coerceAtLeast(0f)
|
||||||
|
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||||
|
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pagerState.animateScrollToPage(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 只有非游客用户才显示"关注"tab
|
||||||
|
if (!AppStore.isGuest) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
|
val existingIndex = currentPositions.indexOfFirst { it.first == 3 }
|
||||||
|
if (existingIndex == -1) {
|
||||||
|
currentPositions.add(Triple(3, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(3, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
UnderlineTabItem(
|
||||||
|
text = stringResource(R.string.index_following),
|
||||||
|
isSelected = pagerState.currentPage == 3,
|
||||||
|
onClick = {
|
||||||
|
tabDebouncer {
|
||||||
|
scope.launch {
|
||||||
|
// 如果当前在关注标签左边的标签页,立即向右滚动显示新闻标签
|
||||||
|
if (pagerState.currentPage < 3 && containerWidth.value > 0) {
|
||||||
|
val newsTab = tabPositions.value.find { it.first == 5 }
|
||||||
|
if (newsTab != null) {
|
||||||
|
val (_, newsPosition, newsWidth) = newsTab
|
||||||
|
val currentScroll = scrollState.value.toFloat()
|
||||||
|
val scrollToPosition = (newsPosition + newsWidth - containerWidth.value).coerceAtLeast(0f)
|
||||||
|
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||||
|
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pagerState.animateScrollToPage(3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 热门标签
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
|
val existingIndex = currentPositions.indexOfFirst { it.first == 4 }
|
||||||
|
if (existingIndex == -1) {
|
||||||
|
currentPositions.add(Triple(4, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(4, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
UnderlineTabItem(
|
||||||
|
text = stringResource(R.string.index_hot),
|
||||||
|
isSelected = pagerState.currentPage == 4,
|
||||||
|
onClick = {
|
||||||
|
tabDebouncer {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 热门标签 (游客模式) - 在游客模式下,热门标签对应第3页
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
|
val existingIndex = currentPositions.indexOfFirst { it.first == 3 }
|
||||||
|
if (existingIndex == -1) {
|
||||||
|
currentPositions.add(Triple(3, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(3, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
UnderlineTabItem(
|
||||||
|
text = stringResource(R.string.index_hot),
|
||||||
|
isSelected = pagerState.currentPage == 3,
|
||||||
|
onClick = {
|
||||||
|
tabDebouncer {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 新闻标签 - 在游客模式下对应第4页,非游客模式下对应第5页
|
||||||
|
val newsPageIndex = if (AppStore.isGuest) 4 else 5
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
val position = with(density) { coordinates.positionInParent().x.toDp().toPx() }
|
||||||
|
val width = with(density) { coordinates.size.width.toDp().toPx() }
|
||||||
|
val currentPositions = tabPositions.value.toMutableList()
|
||||||
|
val existingIndex = currentPositions.indexOfFirst { it.first == newsPageIndex }
|
||||||
|
if (existingIndex == -1) {
|
||||||
|
currentPositions.add(Triple(newsPageIndex, position, width))
|
||||||
|
tabPositions.value = currentPositions.sortedBy { it.first }
|
||||||
|
} else {
|
||||||
|
currentPositions[existingIndex] = Triple(newsPageIndex, position, width)
|
||||||
|
tabPositions.value = currentPositions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
UnderlineTabItem(
|
||||||
|
text = stringResource(R.string.tab_news),
|
||||||
|
isSelected = pagerState.currentPage == newsPageIndex,
|
||||||
|
onClick = {
|
||||||
|
tabDebouncer {
|
||||||
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(newsPageIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 新闻标签 - 在游客模式下对应第4页,非游客模式下对应第5页
|
|
||||||
val newsPageIndex = if (AppStore.isGuest) 4 else 5
|
|
||||||
UnderlineTabItem(
|
|
||||||
text = stringResource(R.string.tab_news),
|
|
||||||
isSelected = pagerState.currentPage == newsPageIndex,
|
|
||||||
onClick = {
|
|
||||||
tabDebouncer {
|
|
||||||
scope.launch {
|
|
||||||
pagerState.animateScrollToPage(newsPageIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索按钮
|
// 搜索按钮
|
||||||
@@ -224,6 +380,50 @@ fun MomentsList() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听页面变化,实现自动滚动
|
||||||
|
LaunchedEffect(pagerState.currentPage, pagerState.currentPageOffsetFraction) {
|
||||||
|
val currentPage = pagerState.currentPage
|
||||||
|
val offsetFraction = pagerState.currentPageOffsetFraction
|
||||||
|
val prevPage = previousPage.value
|
||||||
|
|
||||||
|
// 只在页面切换接近完成时执行(避免滑动过程中频繁触发)
|
||||||
|
val absOffset = kotlin.math.abs(offsetFraction)
|
||||||
|
if (absOffset < 0.1f && containerWidth.value > 0) {
|
||||||
|
// 情况1:从关注标签页左边的标签页(0,1,2)滑动到关注标签页(3)时,向右滚动显示新闻标签(5)
|
||||||
|
if (currentPage == 3 && prevPage < 3 && !AppStore.isGuest) {
|
||||||
|
val newsTab = tabPositions.value.find { it.first == 5 }
|
||||||
|
if (newsTab != null) {
|
||||||
|
val (_, newsPosition, newsWidth) = newsTab
|
||||||
|
val currentScroll = scrollState.value.toFloat()
|
||||||
|
// 计算滚动位置,使新闻标签可见(确保新闻标签的结束位置在可见区域内)
|
||||||
|
val scrollToPosition = (newsPosition + newsWidth - containerWidth.value).coerceAtLeast(0f)
|
||||||
|
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||||
|
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况2:从动态标签页右边的标签页(3,4,5)滑动到动态标签页(2)时,向左滚动显示推荐标签(0)
|
||||||
|
if (currentPage == 2 && prevPage > 2) {
|
||||||
|
val recommendTab = tabPositions.value.find { it.first == 0 }
|
||||||
|
if (recommendTab != null) {
|
||||||
|
val (_, recommendPosition, _) = recommendTab
|
||||||
|
val currentScroll = scrollState.value.toFloat()
|
||||||
|
// 计算滚动位置,使推荐标签可见(确保推荐标签的起始位置在可见区域内)
|
||||||
|
val scrollToPosition = recommendPosition.coerceAtLeast(0f)
|
||||||
|
if (kotlin.math.abs(scrollToPosition - currentScroll) > 1f) {
|
||||||
|
scrollState.animateScrollTo(scrollToPosition.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新上一次的页面索引
|
||||||
|
if (currentPage != prevPage) {
|
||||||
|
previousPage.value = currentPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -310,9 +310,28 @@
|
|||||||
<!-- Edit Profile Extras -->
|
<!-- Edit Profile Extras -->
|
||||||
<string name="mbti_type">MBTI</string>
|
<string name="mbti_type">MBTI</string>
|
||||||
<string name="zodiac">星座</string>
|
<string name="zodiac">星座</string>
|
||||||
|
<string name="change_cover">カバーを変更</string>
|
||||||
|
<string name="personal_intro">自己紹介</string>
|
||||||
|
<string name="error_nickname_empty">ニックネームは空にできません</string>
|
||||||
|
<string name="error_nickname_too_short">ニックネームの長さは3文字未満にできません</string>
|
||||||
|
<string name="error_nickname_too_long">ニックネームの長さは20文字を超えることはできません</string>
|
||||||
|
<string name="error_bio_too_long">自己紹介の長さは100文字を超えることはできません</string>
|
||||||
|
<string name="error_load_profile_failed">ユーザープロフィールの読み込みに失敗しました。もう一度お試しください</string>
|
||||||
<string name="save">保存</string>
|
<string name="save">保存</string>
|
||||||
<string name="choose_mbti">MBTIを選択</string>
|
<string name="choose_mbti">MBTIを選択</string>
|
||||||
<string name="choose_zodiac">星座を選択</string>
|
<string name="choose_zodiac">星座を選択</string>
|
||||||
|
<string name="zodiac_aries">牡羊座</string>
|
||||||
|
<string name="zodiac_taurus">牡牛座</string>
|
||||||
|
<string name="zodiac_gemini">双子座</string>
|
||||||
|
<string name="zodiac_cancer">蟹座</string>
|
||||||
|
<string name="zodiac_leo">獅子座</string>
|
||||||
|
<string name="zodiac_virgo">乙女座</string>
|
||||||
|
<string name="zodiac_libra">天秤座</string>
|
||||||
|
<string name="zodiac_scorpio">蠍座</string>
|
||||||
|
<string name="zodiac_sagittarius">射手座</string>
|
||||||
|
<string name="zodiac_capricorn">山羊座</string>
|
||||||
|
<string name="zodiac_aquarius">水瓶座</string>
|
||||||
|
<string name="zodiac_pisces">魚座</string>
|
||||||
|
|
||||||
<!-- Side Menu -->
|
<!-- Side Menu -->
|
||||||
<string name="scan_qr">QRコードをスキャン</string>
|
<string name="scan_qr">QRコードをスキャン</string>
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
<string name="posts">帖子</string>
|
<string name="posts">帖子</string>
|
||||||
<string name="favourites_upper">收藏</string>
|
<string name="favourites_upper">收藏</string>
|
||||||
<string name="notifications_upper">消息</string>
|
<string name="notifications_upper">消息</string>
|
||||||
<string name="following_upper">关注11</string>
|
<string name="following_upper">关注</string>
|
||||||
<string name="unfollow_upper">取消关注</string>
|
<string name="unfollow_upper">取消关注</string>
|
||||||
<string name="comment_count">%d条评论</string>
|
<string name="comment_count">%d条评论</string>
|
||||||
<string name="post_comment_hint">快来互动吧...</string>
|
<string name="post_comment_hint">快来互动吧...</string>
|
||||||
<string name="follow_upper">关注3</string>
|
<string name="follow_upper">关注</string>
|
||||||
<string name="follow_upper_had">已关注</string>
|
<string name="follow_upper_had">已关注</string>
|
||||||
<string name="login_upper">登录</string>
|
<string name="login_upper">登录</string>
|
||||||
<string name="lets_ride_upper">确认</string>
|
<string name="lets_ride_upper">确认</string>
|
||||||
@@ -334,7 +334,7 @@
|
|||||||
<string name="group_members_admin">管理员</string>
|
<string name="group_members_admin">管理员</string>
|
||||||
<string name="group_members_list">群成员(%d)</string>
|
<string name="group_members_list">群成员(%d)</string>
|
||||||
<string name="group_members_send_message">发消息</string>
|
<string name="group_members_send_message">发消息</string>
|
||||||
<string name="group_members_delete_member">Delete Member</string>
|
<string name="group_members_delete_member">删除成员</string>
|
||||||
|
|
||||||
<!-- Side Menu -->
|
<!-- Side Menu -->
|
||||||
<string name="scan_qr">扫一扫</string>
|
<string name="scan_qr">扫一扫</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user