mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
make grouped logs view more meaningfull
This commit is contained in:
parent
8cebac7905
commit
64cea44dbe
5 changed files with 146 additions and 70 deletions
|
|
@ -23,6 +23,11 @@ pub struct LogLine<'a> {
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
#[serde(rename = "reqId")]
|
#[serde(rename = "reqId")]
|
||||||
pub request_id: TinyAsciiStr<32>,
|
pub request_id: TinyAsciiStr<32>,
|
||||||
|
pub user: TinyAsciiStr<64>,
|
||||||
|
pub method: TinyAsciiStr<12>,
|
||||||
|
pub url: Cow<'a, str>,
|
||||||
|
#[serde(rename = "remoteAddr")]
|
||||||
|
pub remote: TinyAsciiStr<40>,
|
||||||
pub version: &'a str,
|
pub version: &'a str,
|
||||||
pub level: LogLevel,
|
pub level: LogLevel,
|
||||||
pub message: Cow<'a, str>,
|
pub message: Cow<'a, str>,
|
||||||
|
|
@ -137,6 +142,10 @@ impl<'a> LogLine<'a> {
|
||||||
filter.matches(&self.app)
|
filter.matches(&self.app)
|
||||||
|| filter.matches(&self.message)
|
|| filter.matches(&self.message)
|
||||||
|| filter.matches(self.request_id.as_str())
|
|| filter.matches(self.request_id.as_str())
|
||||||
|
|| filter.matches(&self.url)
|
||||||
|
|| filter.matches(&self.method)
|
||||||
|
|| filter.matches(&self.remote)
|
||||||
|
|| filter.matches(&self.user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
108
src/ui/grouped_logs.rs
Normal file
108
src/ui/grouped_logs.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
use crate::app::{App, Filter};
|
||||||
|
use crate::logline::{format_time, LogLine};
|
||||||
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
|
use crate::ui::table::{ScrollbarTable, ScrollbarTableState};
|
||||||
|
use crate::ui::UI_HEADER_SIZE;
|
||||||
|
use ratatui::buffer::Buffer;
|
||||||
|
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||||
|
use ratatui::prelude::{StatefulWidget, Widget};
|
||||||
|
use ratatui::text::Text;
|
||||||
|
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||||
|
|
||||||
|
pub struct GroupedLogs<'a> {
|
||||||
|
line: &'a LogLine<'a>,
|
||||||
|
lines: &'a [usize],
|
||||||
|
app: &'a App<'a>,
|
||||||
|
filter: &'a Filter,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grouped_logs<'a>(
|
||||||
|
app: &'a App<'a>,
|
||||||
|
lines: &'a [usize],
|
||||||
|
filter: &'a Filter,
|
||||||
|
) -> GroupedLogs<'a> {
|
||||||
|
let line = &app.lines[lines[0]];
|
||||||
|
GroupedLogs {
|
||||||
|
line,
|
||||||
|
lines,
|
||||||
|
app,
|
||||||
|
filter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulWidget for GroupedLogs<'_> {
|
||||||
|
type State = ScrollbarTableState;
|
||||||
|
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let lines = self.lines.iter().copied().map(|i| &self.app.lines[i]);
|
||||||
|
|
||||||
|
let par = Paragraph::new(format!(
|
||||||
|
"{}{}{}\n\n{} from {} - Nextcloud {}",
|
||||||
|
self.line
|
||||||
|
.exception
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.exception.as_ref())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
if self.line.exception.is_some() {
|
||||||
|
":\n"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
self.line.message,
|
||||||
|
self.line.level.as_str(),
|
||||||
|
self.line.app,
|
||||||
|
self.line.version,
|
||||||
|
))
|
||||||
|
.wrap(Wrap::default());
|
||||||
|
|
||||||
|
let header = [
|
||||||
|
Text::from("Remote"),
|
||||||
|
Text::from("Method"),
|
||||||
|
Text::from("Url"),
|
||||||
|
Text::from("Request Id"),
|
||||||
|
Text::from("Time").alignment(Alignment::Right),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(Cell::from)
|
||||||
|
.collect::<Row>()
|
||||||
|
.style(TABLE_HEADER_STYLE)
|
||||||
|
.height(1);
|
||||||
|
|
||||||
|
let widths = [
|
||||||
|
Constraint::Min(16),
|
||||||
|
Constraint::Min(8),
|
||||||
|
Constraint::Percentage(100),
|
||||||
|
Constraint::Min(25),
|
||||||
|
Constraint::Length(27),
|
||||||
|
];
|
||||||
|
let table = ScrollbarTable::new(
|
||||||
|
lines.filter(|line| line.matches(self.filter)).map(log_row),
|
||||||
|
widths,
|
||||||
|
)
|
||||||
|
.header(header);
|
||||||
|
|
||||||
|
let layout = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(vec![
|
||||||
|
Constraint::Min(UI_HEADER_SIZE),
|
||||||
|
Constraint::Percentage(100),
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
par.render(layout[0], buf);
|
||||||
|
table.render(layout[1], buf, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_row<'a>(line: &'a LogLine<'a>) -> Row<'a> {
|
||||||
|
Row::new([
|
||||||
|
Text::from(line.remote.as_str()),
|
||||||
|
Text::from(line.method.as_str()),
|
||||||
|
Text::from(line.url.as_ref()),
|
||||||
|
Text::from(line.request_id.as_str()),
|
||||||
|
Text::from(format_time(line.time)).alignment(Alignment::Right),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
@ -2,13 +2,14 @@ use crate::app::App;
|
||||||
use crate::error::UiError;
|
use crate::error::UiError;
|
||||||
use crate::ui::error_list::error_list;
|
use crate::ui::error_list::error_list;
|
||||||
use crate::ui::footer::footer;
|
use crate::ui::footer::footer;
|
||||||
|
use crate::ui::grouped_logs::grouped_logs;
|
||||||
use crate::ui::histogram::UiHistogram;
|
use crate::ui::histogram::UiHistogram;
|
||||||
use crate::ui::match_list::match_list;
|
use crate::ui::match_list::match_list;
|
||||||
use crate::ui::raw_logs::raw_logs;
|
|
||||||
use crate::ui::single_log::single_log;
|
use crate::ui::single_log::single_log;
|
||||||
use crate::ui::single_match::grouped_lines;
|
use crate::ui::single_match::grouped_lines;
|
||||||
use crate::ui::state::{
|
use crate::ui::state::{
|
||||||
ErrorState, LogState, LogsState, MatchListState, MatchState, Mode, UiEvent, UiPage, UiState,
|
ErrorState, GroupedLogsState, LogState, MatchListState, MatchState, Mode, UiEvent, UiPage,
|
||||||
|
UiState,
|
||||||
};
|
};
|
||||||
use ratatui::crossterm::event::{
|
use ratatui::crossterm::event::{
|
||||||
DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyModifiers, MouseButton,
|
DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyModifiers, MouseButton,
|
||||||
|
|
@ -27,9 +28,9 @@ use std::time::Duration;
|
||||||
|
|
||||||
mod error_list;
|
mod error_list;
|
||||||
mod footer;
|
mod footer;
|
||||||
|
mod grouped_logs;
|
||||||
mod histogram;
|
mod histogram;
|
||||||
mod match_list;
|
mod match_list;
|
||||||
mod raw_logs;
|
|
||||||
mod single_log;
|
mod single_log;
|
||||||
mod single_match;
|
mod single_match;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
@ -192,14 +193,14 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
||||||
);
|
);
|
||||||
frame.render_widget(footer(app, state.footer_params()), layout[2]);
|
frame.render_widget(footer(app, state.footer_params()), layout[2]);
|
||||||
}
|
}
|
||||||
UiState::Logs(LogsState {
|
UiState::GroupedLogs(GroupedLogsState {
|
||||||
lines,
|
lines,
|
||||||
table_state,
|
table_state,
|
||||||
filter,
|
filter,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
frame.render_stateful_widget(
|
frame.render_stateful_widget(
|
||||||
raw_logs(app, lines, filter),
|
grouped_logs(app, lines, filter),
|
||||||
layout[0].union(layout[1]),
|
layout[0].union(layout[1]),
|
||||||
table_state,
|
table_state,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
use crate::app::{App, Filter};
|
|
||||||
use crate::logline::{format_time, LogLine};
|
|
||||||
use crate::ui::style::TABLE_HEADER_STYLE;
|
|
||||||
use crate::ui::table::ScrollbarTable;
|
|
||||||
use ratatui::layout::{Alignment, Constraint};
|
|
||||||
use ratatui::text::Text;
|
|
||||||
use ratatui::widgets::{Cell, Row};
|
|
||||||
|
|
||||||
pub fn raw_logs<'a>(app: &'a App<'a>, lines: &[usize], filter: &Filter) -> ScrollbarTable<'a> {
|
|
||||||
let lines = lines.iter().copied().map(|i| &app.lines[i]);
|
|
||||||
let header = [
|
|
||||||
Text::from("Level"),
|
|
||||||
Text::from("App"),
|
|
||||||
Text::from("Message"),
|
|
||||||
Text::from("Time").alignment(Alignment::Right),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.map(Cell::from)
|
|
||||||
.collect::<Row>()
|
|
||||||
.style(TABLE_HEADER_STYLE)
|
|
||||||
.height(1);
|
|
||||||
|
|
||||||
let widths = [
|
|
||||||
Constraint::Min(10),
|
|
||||||
Constraint::Min(20),
|
|
||||||
Constraint::Percentage(100),
|
|
||||||
Constraint::Length(27),
|
|
||||||
];
|
|
||||||
ScrollbarTable::new(
|
|
||||||
lines.filter(|line| line.matches(filter)).map(log_row),
|
|
||||||
widths,
|
|
||||||
)
|
|
||||||
.header(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log_row<'a>(line: &'a LogLine<'a>) -> Row<'a> {
|
|
||||||
Row::new([
|
|
||||||
Text::from(line.level.as_str()),
|
|
||||||
Text::from(line.app.as_ref()),
|
|
||||||
Text::from(line.display()),
|
|
||||||
Text::from(format_time(line.time)).alignment(Alignment::Right),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
@ -12,7 +12,7 @@ use std::iter::once;
|
||||||
pub enum UiState<'a> {
|
pub enum UiState<'a> {
|
||||||
MatchList(MatchListState<'a>),
|
MatchList(MatchListState<'a>),
|
||||||
Match(MatchState<'a>),
|
Match(MatchState<'a>),
|
||||||
Logs(LogsState<'a>),
|
GroupedLogs(GroupedLogsState<'a>),
|
||||||
Log(LogState<'a>),
|
Log(LogState<'a>),
|
||||||
Errors(ErrorState<'a>),
|
Errors(ErrorState<'a>),
|
||||||
Quit,
|
Quit,
|
||||||
|
|
@ -100,7 +100,7 @@ impl<'a> MatchState<'a> {
|
||||||
};
|
};
|
||||||
let lines = selected_line.lines.as_slice();
|
let lines = selected_line.lines.as_slice();
|
||||||
let table_state = ScrollbarTableState::new(lines.len());
|
let table_state = ScrollbarTableState::new(lines.len());
|
||||||
UiState::Logs(LogsState {
|
UiState::GroupedLogs(GroupedLogsState {
|
||||||
lines,
|
lines,
|
||||||
table_state,
|
table_state,
|
||||||
previous: Box::new(self.into()),
|
previous: Box::new(self.into()),
|
||||||
|
|
@ -117,7 +117,7 @@ impl PartialEq for MatchState<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LogsState<'a> {
|
pub struct GroupedLogsState<'a> {
|
||||||
pub lines: &'a [usize],
|
pub lines: &'a [usize],
|
||||||
pub table_state: ScrollbarTableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub previous: Box<UiState<'a>>,
|
pub previous: Box<UiState<'a>>,
|
||||||
|
|
@ -125,24 +125,23 @@ pub struct LogsState<'a> {
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LogsState<'a> {
|
impl<'a> GroupedLogsState<'a> {
|
||||||
fn selected(&self) -> usize {
|
fn selected(&self) -> usize {
|
||||||
self.table_state.selected()
|
self.table_state.selected()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter(self, selected: usize, app: &'a App<'a>) -> UiState<'a> {
|
fn enter(self, selected: usize, app: &'a App<'a>) -> UiState<'a> {
|
||||||
let line = if self.filter.is_empty() {
|
let log = if self.filter.is_empty() {
|
||||||
self.lines[selected]
|
let line = self.lines[selected];
|
||||||
|
&app.lines[line]
|
||||||
} else {
|
} else {
|
||||||
self.lines
|
self.lines
|
||||||
.iter()
|
.iter()
|
||||||
.map(|index| &app.lines[*index])
|
.map(|index| &app.lines[*index])
|
||||||
.filter(|line| line.matches(&self.filter))
|
.filter(|line| line.matches(&self.filter))
|
||||||
.nth(selected)
|
.nth(selected)
|
||||||
.map(|line| line.index)
|
|
||||||
.expect("filtered select out of bounds")
|
.expect("filtered select out of bounds")
|
||||||
};
|
};
|
||||||
let log = &app.lines[line];
|
|
||||||
let raw_line = app.get_line(log.index).unwrap();
|
let raw_line = app.get_line(log.index).unwrap();
|
||||||
let full_line = parse_line_full(raw_line).unwrap();
|
let full_line = parse_line_full(raw_line).unwrap();
|
||||||
let trace_len = if let Some(exception) = &full_line.exception {
|
let trace_len = if let Some(exception) = &full_line.exception {
|
||||||
|
|
@ -161,7 +160,7 @@ impl<'a> LogsState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for LogsState<'_> {
|
impl PartialEq for GroupedLogsState<'_> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.lines == other.lines
|
self.lines == other.lines
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +208,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::Quit | UiState::MatchList(_) => UiPage::MatchList,
|
UiState::Quit | UiState::MatchList(_) => UiPage::MatchList,
|
||||||
UiState::Match(_) => UiPage::Match,
|
UiState::Match(_) => UiPage::Match,
|
||||||
UiState::Logs(_) => UiPage::Logs,
|
UiState::GroupedLogs(_) => UiPage::Logs,
|
||||||
UiState::Log(_) => UiPage::Log,
|
UiState::Log(_) => UiPage::Log,
|
||||||
UiState::Errors(_) => UiPage::Errors,
|
UiState::Errors(_) => UiPage::Errors,
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +218,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => state.mode,
|
UiState::MatchList(state) => state.mode,
|
||||||
UiState::Match(state) => state.mode,
|
UiState::Match(state) => state.mode,
|
||||||
UiState::Logs(state) => state.mode,
|
UiState::GroupedLogs(state) => state.mode,
|
||||||
_ => Mode::Normal,
|
_ => Mode::Normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +227,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => state.mode = mode,
|
UiState::MatchList(state) => state.mode = mode,
|
||||||
UiState::Match(state) => state.mode = mode,
|
UiState::Match(state) => state.mode = mode,
|
||||||
UiState::Logs(state) => state.mode = mode,
|
UiState::GroupedLogs(state) => state.mode = mode,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +236,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&state.filter),
|
UiState::MatchList(state) => Some(&state.filter),
|
||||||
UiState::Match(state) => Some(&state.filter),
|
UiState::Match(state) => Some(&state.filter),
|
||||||
UiState::Logs(state) => Some(&state.filter),
|
UiState::GroupedLogs(state) => Some(&state.filter),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +245,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&mut state.filter),
|
UiState::MatchList(state) => Some(&mut state.filter),
|
||||||
UiState::Match(state) => Some(&mut state.filter),
|
UiState::Match(state) => Some(&mut state.filter),
|
||||||
UiState::Logs(state) => Some(&mut state.filter),
|
UiState::GroupedLogs(state) => Some(&mut state.filter),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +254,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&state.table_state),
|
UiState::MatchList(state) => Some(&state.table_state),
|
||||||
UiState::Match(state) => Some(&state.table_state),
|
UiState::Match(state) => Some(&state.table_state),
|
||||||
UiState::Logs(state) => Some(&state.table_state),
|
UiState::GroupedLogs(state) => Some(&state.table_state),
|
||||||
UiState::Log(state) => Some(&state.table_state),
|
UiState::Log(state) => Some(&state.table_state),
|
||||||
UiState::Errors(state) => Some(&state.table_state),
|
UiState::Errors(state) => Some(&state.table_state),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -266,7 +265,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&mut state.table_state),
|
UiState::MatchList(state) => Some(&mut state.table_state),
|
||||||
UiState::Match(state) => Some(&mut state.table_state),
|
UiState::Match(state) => Some(&mut state.table_state),
|
||||||
UiState::Logs(state) => Some(&mut state.table_state),
|
UiState::GroupedLogs(state) => Some(&mut state.table_state),
|
||||||
UiState::Log(state) => Some(&mut state.table_state),
|
UiState::Log(state) => Some(&mut state.table_state),
|
||||||
UiState::Errors(state) => Some(&mut state.table_state),
|
UiState::Errors(state) => Some(&mut state.table_state),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -324,7 +323,7 @@ impl<'a> UiState<'a> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(_) => UI_HEADER_SIZE + 1,
|
UiState::MatchList(_) => UI_HEADER_SIZE + 1,
|
||||||
UiState::Match(_) => UI_HEADER_SIZE + 1,
|
UiState::Match(_) => UI_HEADER_SIZE + 1,
|
||||||
UiState::Logs(_) => 0,
|
UiState::GroupedLogs(_) => UI_HEADER_SIZE + 1,
|
||||||
UiState::Log(_) => 0,
|
UiState::Log(_) => 0,
|
||||||
UiState::Errors(_) => 0,
|
UiState::Errors(_) => 0,
|
||||||
UiState::Quit => 0,
|
UiState::Quit => 0,
|
||||||
|
|
@ -387,12 +386,14 @@ impl<'a> UiState<'a> {
|
||||||
(true, state.enter(selected, app))
|
(true, state.enter(selected, app))
|
||||||
}
|
}
|
||||||
(UiState::Match(state), UiEvent::Enter(selected)) => (true, state.enter(selected, app)),
|
(UiState::Match(state), UiEvent::Enter(selected)) => (true, state.enter(selected, app)),
|
||||||
(UiState::Logs(state), UiEvent::Select) => {
|
(UiState::GroupedLogs(state), UiEvent::Select) => {
|
||||||
let selected = state.selected();
|
let selected = state.selected();
|
||||||
(true, state.enter(selected, app))
|
(true, state.enter(selected, app))
|
||||||
}
|
}
|
||||||
(UiState::Logs(state), UiEvent::Enter(selected)) => (true, state.enter(selected, app)),
|
(UiState::GroupedLogs(state), UiEvent::Enter(selected)) => {
|
||||||
(UiState::Logs(state), UiEvent::Copy) => {
|
(true, state.enter(selected, app))
|
||||||
|
}
|
||||||
|
(UiState::GroupedLogs(state), UiEvent::Copy) => {
|
||||||
let selected = state.selected();
|
let selected = state.selected();
|
||||||
let mut table_state = TableState::default();
|
let mut table_state = TableState::default();
|
||||||
table_state.select(Some(0));
|
table_state.select(Some(0));
|
||||||
|
|
@ -400,7 +401,7 @@ impl<'a> UiState<'a> {
|
||||||
let line = &app.lines[state.lines[selected]];
|
let line = &app.lines[state.lines[selected]];
|
||||||
let raw = app.get_line(line.index).unwrap_or_default();
|
let raw = app.get_line(line.index).unwrap_or_default();
|
||||||
copy_osc(raw);
|
copy_osc(raw);
|
||||||
(false, UiState::Logs(state))
|
(false, UiState::GroupedLogs(state))
|
||||||
}
|
}
|
||||||
(UiState::Log(state), UiEvent::Copy) => {
|
(UiState::Log(state), UiEvent::Copy) => {
|
||||||
let raw = app.get_line(state.log.index).unwrap_or_default();
|
let raw = app.get_line(state.log.index).unwrap_or_default();
|
||||||
|
|
@ -455,7 +456,7 @@ impl<'a> UiState<'a> {
|
||||||
|
|
||||||
(
|
(
|
||||||
UiState::Match(MatchState { previous, .. })
|
UiState::Match(MatchState { previous, .. })
|
||||||
| UiState::Logs(LogsState { previous, .. })
|
| UiState::GroupedLogs(GroupedLogsState { previous, .. })
|
||||||
| UiState::Log(LogState { previous, .. })
|
| UiState::Log(LogState { previous, .. })
|
||||||
| UiState::Errors(ErrorState { previous, .. }),
|
| UiState::Errors(ErrorState { previous, .. }),
|
||||||
UiEvent::Back,
|
UiEvent::Back,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue