feat: paginate, sync, and display branched sessions (#563)
* mobile: 以 Desktop Sessions 作为聊天历史源 * mobile: 修复快速连接崩溃与无标题会话隐藏 * mobile: 对齐 Desktop 标题并恢复完整历史 * mobile: 分离 Desktop 存储 ID 与运行 Session ID * mobile: 将新对话持久化为 Desktop Session * mobile: 渲染 Desktop 本地媒体 * feat: 分页加载并同步当前会话 * fix: 显示分支会话名称与层级 * fix: align PR with upstream app contract * fix: preserve upstream session RPC lifecycle
This commit is contained in:
1 parent
306861a222
commit
3052db7d65
8 files changed
+405
-79
No files matched your search
@@ -76,6 +76,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.m57.hermescontrol.ChatScreen
|
||||
import com.m57.hermescontrol.NavigationController
|
||||
import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.model.SessionInfo
|
||||
import com.m57.hermescontrol.data.model.flattenSessionTree
|
||||
import com.m57.hermescontrol.theme.LocalHermesStatusColors
|
||||
import com.m57.hermescontrol.theme.LocalSpacing
|
||||
import com.m57.hermescontrol.ui.common.EmptyState
|
||||
@@ -170,11 +172,17 @@ fun SessionsScreen(
|
||||
var query by remember { mutableStateOf("") }
|
||||
var pruneDays by remember { mutableStateOf("7") }
|
||||
|
||||
val sessionTree = remember(state.sessions) { flattenSessionTree(state.sessions) }
|
||||
val filteredSessions =
|
||||
remember(query, state.sessions) {
|
||||
state.sessions.filter { session ->
|
||||
session.title?.contains(query, ignoreCase = true) == true ||
|
||||
session.status?.contains(query, ignoreCase = true) == true
|
||||
remember(query, sessionTree) {
|
||||
if (query.isBlank()) {
|
||||
sessionTree
|
||||
} else {
|
||||
sessionTree.filter { item ->
|
||||
item.displayTitle.contains(query, ignoreCase = true) ||
|
||||
item.session.preview?.contains(query, ignoreCase = true) == true ||
|
||||
item.session.status?.contains(query, ignoreCase = true) == true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,9 +438,13 @@ fun SessionsScreen(
|
||||
contentPadding = listContentPadding,
|
||||
verticalArrangement = listItemSpacing,
|
||||
) {
|
||||
items(filteredSessions, key = { it.id }) { session ->
|
||||
items(filteredSessions, key = { it.session.id }) { item ->
|
||||
val session = item.session
|
||||
SessionCard(
|
||||
session = session,
|
||||
displayTitle = item.displayTitle,
|
||||
depth = item.depth,
|
||||
branchStem = item.branchStem,
|
||||
query = query,
|
||||
isSelecting = state.isSelecting,
|
||||
isSelected = session.id in state.selectedIds,
|
||||
@@ -589,7 +601,10 @@ fun SessionsScreen(
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun SessionCard(
|
||||
session: com.m57.hermescontrol.data.model.SessionInfo,
|
||||
session: SessionInfo,
|
||||
displayTitle: String,
|
||||
depth: Int,
|
||||
branchStem: String?,
|
||||
query: String,
|
||||
isSelecting: Boolean,
|
||||
isSelected: Boolean,
|
||||
@@ -603,7 +618,6 @@ private fun SessionCard(
|
||||
) {
|
||||
val spacing = LocalSpacing.current
|
||||
val statusColors = LocalHermesStatusColors.current
|
||||
val displayTitle = session.title?.takeIf { it.isNotBlank() } ?: stringResource(R.string.history_untitled)
|
||||
val isActive = session.status?.lowercase() == "active" || session.status?.lowercase() == "streaming"
|
||||
val srcIcon = sourceIcon(session.source)
|
||||
|
||||
@@ -611,6 +625,7 @@ private fun SessionCard(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = (depth * 16).dp)
|
||||
.testTag("session_card_${session.id}")
|
||||
.combinedClickable(
|
||||
onClick = onCardClick,
|
||||
@@ -643,6 +658,16 @@ private fun SessionCard(
|
||||
Spacer(modifier = Modifier.width(spacing.sm))
|
||||
}
|
||||
|
||||
// Branch tree stem
|
||||
if (branchStem != null && !isSelecting) {
|
||||
Text(
|
||||
text = branchStem,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(spacing.sm))
|
||||
}
|
||||
|
||||
// Source icon
|
||||
if (srcIcon != null && !isSelecting) {
|
||||
Icon(
|
||||
|
||||
Reference in new issue
Block a user