Improve Compose group paging (#6105)
This commit is contained in:
1 parent
3c9336f671
commit
26099c198e
2 files changed
+68
-56
No files matched your search
@@ -333,25 +333,14 @@ object CoreServiceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val endpoint = if (time >= 0) SpeedtestManager.getRemoteIPInfo() else null
|
||||||
val result = ConnectionTestResult(
|
val result = ConnectionTestResult(
|
||||||
delayMillis = time,
|
delayMillis = time,
|
||||||
errorMessage = errorStr,
|
errorMessage = errorStr,
|
||||||
|
country = endpoint?.country,
|
||||||
|
ipAddress = endpoint?.ipAddress,
|
||||||
)
|
)
|
||||||
MessageHelper.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_RESULT, result)
|
MessageHelper.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_RESULT, result)
|
||||||
|
|
||||||
// Only fetch IP info if the delay test was successful
|
|
||||||
if (time >= 0) {
|
|
||||||
SpeedtestManager.getRemoteIPInfo()?.let { ip ->
|
|
||||||
MessageHelper.sendMsg2UI(
|
|
||||||
service,
|
|
||||||
AppConfig.MSG_MEASURE_DELAY_RESULT,
|
|
||||||
result.copy(
|
|
||||||
country = ip.country,
|
|
||||||
ipAddress = ip.ipAddress,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
package com.v2ray.ang.ui.main
|
package com.v2ray.ang.ui.main
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.widthIn
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PrimaryScrollableTabRow
|
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.TabRowDefaults
|
|
||||||
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.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
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.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.v2ray.ang.dto.GroupMapItem
|
import com.v2ray.ang.dto.GroupMapItem
|
||||||
@@ -28,32 +36,34 @@ fun GroupTabBar(
|
|||||||
onTabClick: (Int) -> Unit,
|
onTabClick: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
PrimaryScrollableTabRow(
|
val selectedIndex = selectedTabIndex.coerceIn(0, groups.lastIndex)
|
||||||
selectedTabIndex = selectedTabIndex.coerceIn(0, groups.lastIndex),
|
val listState = rememberLazyListState(initialFirstVisibleItemIndex = selectedIndex)
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.95f),
|
LaunchedEffect(selectedIndex) {
|
||||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
if (listState.layoutInfo.visibleItemsInfo.none { it.index == selectedIndex }) {
|
||||||
edgePadding = 16.dp,
|
listState.animateScrollToItem(selectedIndex)
|
||||||
minTabWidth = 56.dp,
|
}
|
||||||
indicator = {
|
}
|
||||||
TabRowDefaults.PrimaryIndicator(
|
|
||||||
modifier = Modifier
|
LazyRow(
|
||||||
.tabIndicatorOffset(
|
state = listState,
|
||||||
selectedTabIndex = selectedTabIndex.coerceIn(0, groups.lastIndex),
|
modifier = modifier
|
||||||
matchContentSize = true
|
.fillMaxWidth()
|
||||||
)
|
.background(MaterialTheme.colorScheme.surface.copy(alpha = 0.95f)),
|
||||||
.clip(RoundedCornerShape(3.dp)),
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
width = Dp.Unspecified,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
color = MaterialTheme.colorScheme.secondary
|
|
||||||
)
|
|
||||||
},
|
|
||||||
divider = {}
|
|
||||||
) {
|
) {
|
||||||
groups.forEachIndexed { index, group ->
|
itemsIndexed(
|
||||||
|
items = groups,
|
||||||
|
key = { _, group -> group.id },
|
||||||
|
) { index, group ->
|
||||||
|
val serverFlow = remember(group.id, mainViewModel) {
|
||||||
|
mainViewModel.serversForGroup(group.id)
|
||||||
|
}
|
||||||
GroupTabItem(
|
GroupTabItem(
|
||||||
group = group,
|
group = group,
|
||||||
selected = index == selectedTabIndex,
|
selected = index == selectedIndex,
|
||||||
serverFlowProvider = { mainViewModel.serversForGroup(group.id) },
|
serverFlow = serverFlow,
|
||||||
onClick = { onTabClick(index) }
|
onClick = { onTabClick(index) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -64,26 +74,39 @@ fun GroupTabBar(
|
|||||||
private fun GroupTabItem(
|
private fun GroupTabItem(
|
||||||
group: GroupMapItem,
|
group: GroupMapItem,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
serverFlowProvider: () -> StateFlow<List<ServersCache>>,
|
serverFlow: StateFlow<List<ServersCache>>,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
val serverFlow = remember(group.id) { serverFlowProvider() }
|
|
||||||
val servers by serverFlow.collectAsStateWithLifecycle()
|
val servers by serverFlow.collectAsStateWithLifecycle()
|
||||||
Tab(
|
val text = if (group.id.isEmpty()) {
|
||||||
selected = selected,
|
group.remarks
|
||||||
onClick = onClick,
|
} else {
|
||||||
text = {
|
"${group.remarks} (${servers.size})"
|
||||||
val text = if (group.id.isEmpty()) {
|
}
|
||||||
group.remarks
|
|
||||||
} else {
|
Box(Modifier.widthIn(min = 56.dp)) {
|
||||||
"${group.remarks} (${servers.size})"
|
Tab(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = Modifier.heightIn(min = 48.dp),
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
maxLines = 1,
|
||||||
|
softWrap = false,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Text(
|
)
|
||||||
text = text,
|
if (selected) {
|
||||||
maxLines = 1,
|
Box(
|
||||||
softWrap = false,
|
Modifier
|
||||||
overflow = TextOverflow.Ellipsis
|
.align(Alignment.BottomCenter)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(3.dp)
|
||||||
|
.clip(RoundedCornerShape(3.dp))
|
||||||
|
.background(MaterialTheme.colorScheme.secondary)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user