[ADDED] Sonnet 3.7 generated improved log screen#196
Conversation
Screen_recording_20250301_150401.mp4 |
|
The log screen looks good. However, the search feature does not work well for this. Please remove the search feature from AlertCheckLogViewerScreen.kt |
Code Coverage Report |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #196 +/- ##
========================================
+ Coverage 3.23% 5.97% +2.73%
========================================
Files 72 74 +2
Lines 1636 1690 +54
Branches 99 114 +15
========================================
+ Hits 53 101 +48
- Misses 1577 1583 +6
Partials 6 6 🚀 New features to boost your workflow:
|
|
The fliter chip items are too many and does not fit in one row. Is there any way to break the filter chips to multiple lines if they don't fit? Follow the best practices. |
|
You're right that filter chips can overflow when there are too many items in a single row. I'll modify the code to allow filter chips to wrap to multiple lines. Here's an improved implementation using import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun NotifierTypeFilterChips(
currentState: AlertCheckLogViewerScreen.State,
onFilterByNotifierType: (NotifierType?) -> Unit,
modifier: Modifier = Modifier,
) {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
) {
FilterChip(
selected = currentState.selectedNotifierType == null,
onClick = { onFilterByNotifierType(null) },
label = { Text("All") },
)
// Create a chip for each notifier type
NotifierType.entries.forEach { notifierType ->
FilterChip(
selected = currentState.selectedNotifierType == notifierType,
onClick = { onFilterByNotifierType(notifierType) },
label = { Text(notifierType.displayName) },
)
}
}
}Then replace your original Row with a call to this composable: NotifierTypeFilterChips(
currentState = currentState,
onFilterByNotifierType = onFilterByNotifierType,
)This implementation:
|
|
On the Please add animation following the material 3 design best practices. |
|
Looking at your Here's the updated code with animations: @OptIn(ExperimentalLayoutApi::class)
@Composable
private fun ActiveFiltersSection(
showTriggeredOnly: Boolean,
alertType: AlertType?,
notifierType: NotifierType?,
dateRange: Pair<Long?, Long?>,
onClearFilter: (String) -> Unit,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMedium
)
),
) {
Text(
text = "Active Filters:",
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(bottom = 4.dp),
)
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.padding(vertical = 4.dp),
) {
// Triggered Only Filter
AnimatedVisibility(
visible = showTriggeredOnly,
enter = fadeIn() + expandHorizontally(),
exit = fadeOut() + shrinkHorizontally()
) {
FilterChip(
selected = true,
onClick = { onClearFilter("triggered") },
label = { Text("Triggered Only") },
trailingIcon = {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Clear Filter",
modifier = Modifier.size(16.dp),
)
},
)
}
// Alert Type Filter
AnimatedVisibility(
visible = alertType != null,
enter = fadeIn() + expandHorizontally(),
exit = fadeOut() + shrinkHorizontally()
) {
FilterChip(
selected = true,
onClick = { onClearFilter("alertType") },
label = {
Text(
when (alertType) {
AlertType.BATTERY -> "Battery"
AlertType.STORAGE -> "Storage"
else -> ""
},
)
},
trailingIcon = {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Clear Filter",
modifier = Modifier.size(16.dp),
)
},
)
}
// Notifier Type Filter
AnimatedVisibility(
visible = notifierType != null,
enter = fadeIn() + expandHorizontally(),
exit = fadeOut() + shrinkHorizontally()
) {
FilterChip(
selected = true,
onClick = { onClearFilter("notifierType") },
label = { Text(notifierType?.displayName ?: "") },
trailingIcon = {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Clear Filter",
modifier = Modifier.size(16.dp),
)
},
)
}
// Date Range Filter
AnimatedVisibility(
visible = dateRange.first != null || dateRange.second != null,
enter = fadeIn() + expandHorizontally(),
exit = fadeOut() + shrinkHorizontally()
) {
FilterChip(
selected = true,
onClick = { onClearFilter("dateRange") },
label = {
val dateText =
when {
dateRange.first != null && dateRange.second != null -> "Date Range"
dateRange.first != null ->
"From ${
SimpleDateFormat("MMM dd", Locale.getDefault())
.format(Date(dateRange.first!!))
}"
else ->
"Until ${
SimpleDateFormat("MMM dd", Locale.getDefault())
.format(Date(dateRange.second!!))
}"
}
Text(dateText)
},
trailingIcon = {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Clear Filter",
modifier = Modifier.size(16.dp),
)
},
)
}
}
}
}Changes made:
Make sure to import these additional dependencies: import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring |
|
When the |
|
To enable animation for adding and removing items in a LazyColumn, you'll need to use Jetpack Compose's Here's how to implement it: import androidx.compose.foundation.lazy.items
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
@OptIn(ExperimentalFoundationApi::class)
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp),
) {
item(key = "logs_summary") {
LogsSummaryInfo(
totalLogs = state.logs.size,
filteredLogs = state.filteredLogs.size,
checkIntervalMinutes = state.checkIntervalMinutes,
hasActiveFilters = hasActiveFilters,
onClearFilters = {
if (hasActiveFilters) {
state.eventSink(AlertCheckLogViewerScreen.Event.ClearFilters)
}
},
modifier = Modifier.animateItemPlacement(
animationSpec = tween(durationMillis = 300)
)
)
}
// Active filters display
if (hasActiveFilters) {
item(key = "active_filters") {
ActiveFiltersSection(
showTriggeredOnly = state.showTriggeredOnly,
alertType = state.selectedAlertType,
notifierType = state.selectedNotifierType,
dateRange = state.dateRange,
onClearFilter = { filter ->
when (filter) {
"triggered" -> state.eventSink(AlertCheckLogViewerScreen.Event.ToggleTriggeredOnly)
"alertType" -> state.eventSink(AlertCheckLogViewerScreen.Event.FilterByAlertType(null))
"notifierType" -> state.eventSink(AlertCheckLogViewerScreen.Event.FilterByNotifierType(null))
"dateRange" -> state.eventSink(AlertCheckLogViewerScreen.Event.FilterByDateRange(null, null))
}
},
modifier = Modifier.animateItemPlacement(
animationSpec = tween(durationMillis = 300)
)
)
}
}
items(
count = state.filteredLogs.size,
key = { index -> state.filteredLogs[index].checkedOn },
) { index ->
val log = state.filteredLogs[index]
val isExpanded = expandedLogId == log.checkedOn
LogItemCard(
log = log,
isExpanded = isExpanded,
onClick = {
expandedLogId = if (isExpanded) -1L else log.checkedOn
},
onCopyDetails = {
// Copy functionality
},
modifier = Modifier.animateItemPlacement(
animationSpec = tween(durationMillis = 300)
)
)
}
}Key changes made:
Don't forget to update your @Composable
private fun LogItemCard(
log: AlertCheckLog,
isExpanded: Boolean,
onClick: () -> Unit,
onCopyDetails: () -> Unit,
modifier: Modifier = Modifier
) {
// Use the provided modifier combined with your existing modifiers
Card(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick),
// rest of implementation
) {
// ...
}
}Make similar adjustments to your |
|
is there a |
|
How about using |
|
To animate items being added or removed from a Here's how to implement it for your import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.animation.core.tween
@OptIn(ExperimentalFoundationApi::class)
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp),
) {
item(key = "logs_summary") {
LogsSummaryInfo(
totalLogs = state.logs.size,
filteredLogs = state.filteredLogs.size,
checkIntervalMinutes = state.checkIntervalMinutes,
hasActiveFilters = hasActiveFilters,
onClearFilters = {
if (hasActiveFilters) {
state.eventSink(AlertCheckLogViewerScreen.Event.ClearFilters)
}
},
modifier = Modifier.animateItemPlacement(
animationSpec = tween(durationMillis = 300)
)
)
}
// Active filters display
if (hasActiveFilters) {
item(key = "active_filters") {
ActiveFiltersSection(
showTriggeredOnly = state.showTriggeredOnly,
alertType = state.selectedAlertType,
notifierType = state.selectedNotifierType,
dateRange = state.dateRange,
onClearFilter = { filter ->
when (filter) {
"triggered" -> state.eventSink(AlertCheckLogViewerScreen.Event.ToggleTriggeredOnly)
"alertType" -> state.eventSink(AlertCheckLogViewerScreen.Event.FilterByAlertType(null))
"notifierType" -> state.eventSink(AlertCheckLogViewerScreen.Event.FilterByNotifierType(null))
"dateRange" -> state.eventSink(AlertCheckLogViewerScreen.Event.FilterByDateRange(null, null))
}
},
modifier = Modifier.animateItemPlacement(
animationSpec = tween(durationMillis = 300)
)
)
}
}
items(
count = state.filteredLogs.size,
key = { index -> state.filteredLogs[index].checkedOn },
) { index ->
val log = state.filteredLogs[index]
val isExpanded = expandedLogId == log.checkedOn
LogItemCard(
log = log,
isExpanded = isExpanded,
onClick = {
expandedLogId = if (isExpanded) -1L else log.checkedOn
},
onCopyDetails = {
// Copy functionality
},
modifier = Modifier.animateItemPlacement(
animationSpec = tween(durationMillis = 300)
)
)
}
}Don't forget to update the function signature of your components to accept a modifier parameter: @OptIn(ExperimentalLayoutApi::class)
@Composable
private fun ActiveFiltersSection(
showTriggeredOnly: Boolean,
alertType: AlertType?,
notifierType: NotifierType?,
dateRange: Pair<Long?, Long?>,
onClearFilter: (String) -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMedium
)
),
) {
// Existing code...
}
}This approach will animate both the addition/removal of the active filters section as well as the position changes of other items when the list content changes. |
Take a look into AlertCheckLogViewerScreen.kt and improve the UI/UX of the screen. Add filtering and other applicable functionality to make it better for users.
This pull request includes several changes to the
dev.hossain.remotenotifyproject, focusing on improving the user interface and refactoring utility functions. The most important changes include the addition of a new filter UI for alert logs, updating notification sender documentation, and refactoring time formatting utilities.UI Enhancements:
FilterLogsUi.kt, allowing users to filter logs by alert type, notification method, and date range.Documentation Updates:
NotificationSenderinterface documentation to include more details about the different types of remote destinations.Refactoring:
formatTimeElapsedandformatDurationto a new fileDateTimeFormatter.ktand removing the oldformatTimeDurationfunction [1] [2].formatTimeElapsedfunction inLastCheckStatusCardUi.kt[1] [2] [3].Minor Changes:
AlertCheckLogDao.ktfrom 20 to 30.filter_alt_24dp.xml.