feat: 优化AI聊天侧栏,添加搜索过滤、时间标签、圆角卡片和窄宽度

This commit is contained in:
HapeLee
2026-06-27 10:15:21 +08:00
parent 3594dc2594
commit 6d8660ee5b
@@ -45,8 +45,10 @@ import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.Lightbulb
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.DismissibleNavigationDrawer
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalDrawerSheet
@@ -70,6 +72,7 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
@@ -83,10 +86,12 @@ import io.legado.app.domain.model.AiMessagePart
import io.legado.app.domain.model.AiReasoningLevel
import io.legado.app.domain.model.AiMessageRole
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.AppTextField
import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.button.series.MediumTonalButton
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
import io.legado.app.ui.widget.components.card.GlassCard
import io.legado.app.ui.widget.components.card.NormalCard
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.image.cover.CoilBookCover
import io.legado.app.ui.widget.components.text.AppText
@@ -99,6 +104,10 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.TimeUnit
@Composable
fun AiChatRouteScreen(
@@ -157,6 +166,7 @@ fun AiChatScreen(
}
) {
AppScaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {
GlassMediumFlexibleTopAppBar(
@@ -461,7 +471,21 @@ private fun RecentChatsDrawer(
onNewChat: () -> Unit,
onSelectConversation: (String) -> Unit
) {
ModalDrawerSheet {
var searchQuery by remember { mutableStateOf("") }
val filteredConversations = remember(conversations, searchQuery) {
if (searchQuery.isBlank()) {
conversations
} else {
conversations.filter {
it.title.contains(searchQuery, ignoreCase = true)
}
}
}
ModalDrawerSheet(
modifier = Modifier.width(280.dp),
drawerContainerColor = LegadoTheme.colorScheme.surfaceContainerLow
) {
Row(
modifier = Modifier
.fillMaxWidth()
@@ -480,34 +504,79 @@ private fun RecentChatsDrawer(
contentDescription = stringResource(R.string.ai_new_chat)
)
}
AppTextField(
value = searchQuery,
onValueChange = { searchQuery = it },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 4.dp),
backgroundColor = LegadoTheme.colorScheme.surface,
label = stringResource(R.string.search),
singleLine = true
)
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(2.dp)
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(conversations, key = { it.id }) { conversation ->
val background = if (conversation.isSelected) {
LegadoTheme.colorScheme.primaryContainer
} else {
LegadoTheme.colorScheme.surface
}
Column(
modifier = Modifier
.fillMaxWidth()
.background(background)
.clickable(onClick = { onSelectConversation(conversation.id) })
.padding(horizontal = 16.dp, vertical = 12.dp)
items(filteredConversations, key = { it.id }) { conversation ->
val isSelected = conversation.isSelected
NormalCard(
modifier = Modifier.fillMaxWidth(),
onClick = { onSelectConversation(conversation.id) },
containerColor = if (isSelected) {
LegadoTheme.colorScheme.primaryContainer
} else {
LegadoTheme.colorScheme.surface
}
) {
AppText(
text = conversation.title,
style = LegadoTheme.typography.bodyLarge,
maxLines = 2
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 10.dp)
) {
AppText(
text = conversation.title,
style = LegadoTheme.typography.bodyMedium,
maxLines = 2,
color = if (isSelected) {
LegadoTheme.colorScheme.onPrimaryContainer
} else {
LegadoTheme.colorScheme.onSurface
}
)
Spacer(modifier = Modifier.height(2.dp))
AppText(
text = formatRelativeTime(conversation.updatedAt),
style = LegadoTheme.typography.labelSmall,
color = LegadoTheme.colorScheme.outline
)
}
}
}
}
}
}
private fun formatRelativeTime(timestamp: Long): String {
val now = System.currentTimeMillis()
val diff = now - timestamp
val minutes = TimeUnit.MILLISECONDS.toMinutes(diff)
val hours = TimeUnit.MILLISECONDS.toHours(diff)
val days = TimeUnit.MILLISECONDS.toDays(diff)
return when {
minutes < 1 -> "刚刚"
minutes < 60 -> "${minutes}分钟前"
hours < 24 -> "${hours}小时前"
days < 7 -> "${days}天前"
days < 30 -> "${days / 7}周前"
else -> {
val sdf = SimpleDateFormat("MM/dd", Locale.getDefault())
sdf.format(Date(timestamp))
}
}
}
@Composable
private fun ChatMessageItem(
message: AiChatMessageUi,