Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions Rnwood.Smtp4dev/TUI/ManagementDialogs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ public class UsersDialog : Dialog
private readonly string dataDir;
private ListView userListView;
private SettingsManager settingsManager;
private ServerOptions serverOptions;

public UsersDialog(IHost host, string dataDir) : base("Manage Users", 60, 20)
{
this.host = host;
this.dataDir = dataDir;
this.settingsManager = new SettingsManager(host, dataDir);
this.serverOptions = settingsManager.GetServerOptions();
CreateUI();
}

Expand Down Expand Up @@ -65,7 +67,6 @@ private void CreateUI()

private void RefreshList()
{
var serverOptions = settingsManager.GetServerOptions();
var users = serverOptions.Users?.Select(u => u.Username).ToList() ?? new System.Collections.Generic.List<string>();
userListView.SetSource(users);
}
Expand All @@ -89,7 +90,6 @@ private void AddUser()

if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
var serverOptions = settingsManager.GetServerOptions();
var usersList = serverOptions.Users?.ToList() ?? new List<UserOptions>();

usersList.Add(new UserOptions
Expand All @@ -99,7 +99,6 @@ private void AddUser()
});

serverOptions.Users = usersList.ToArray();
settingsManager.SaveSettings(serverOptions, settingsManager.GetRelayOptions()).Wait();
RefreshList();
Application.RequestStop();
}
Expand All @@ -121,7 +120,6 @@ private void RemoveUser()
{
if (userListView.SelectedItem >= 0)
{
var serverOptions = settingsManager.GetServerOptions();
if (serverOptions.Users != null && userListView.SelectedItem < serverOptions.Users.Length)
{
var username = serverOptions.Users[userListView.SelectedItem].Username;
Expand All @@ -134,7 +132,6 @@ private void RemoveUser()
var usersList = serverOptions.Users.ToList();
usersList.RemoveAt(userListView.SelectedItem);
serverOptions.Users = usersList.ToArray();
settingsManager.SaveSettings(serverOptions, settingsManager.GetRelayOptions()).Wait();
RefreshList();
}
}
Expand All @@ -151,12 +148,14 @@ public class MailboxesDialog : Dialog
private readonly string dataDir;
private ListView mailboxListView;
private SettingsManager settingsManager;
private ServerOptions serverOptions;

public MailboxesDialog(IHost host, string dataDir) : base("Manage Mailboxes", 60, 20)
{
this.host = host;
this.dataDir = dataDir;
this.settingsManager = new SettingsManager(host, dataDir);
this.serverOptions = settingsManager.GetServerOptions();
CreateUI();
}

Expand Down Expand Up @@ -200,7 +199,6 @@ private void CreateUI()

private void RefreshList()
{
var serverOptions = settingsManager.GetServerOptions();
var mailboxes = serverOptions.Mailboxes?.Select(m => $"{m.Name} ({m.Recipients})").ToList()
?? new System.Collections.Generic.List<string>();
mailboxListView.SetSource(mailboxes);
Expand All @@ -225,7 +223,6 @@ private void AddMailbox()

if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(pattern))
{
var serverOptions = settingsManager.GetServerOptions();
var mailboxesList = serverOptions.Mailboxes?.ToList() ?? new List<MailboxOptions>();

mailboxesList.Add(new MailboxOptions
Expand All @@ -235,8 +232,6 @@ private void AddMailbox()
});

serverOptions.Mailboxes = mailboxesList.ToArray();

settingsManager.SaveSettings(serverOptions, settingsManager.GetRelayOptions()).Wait();
RefreshList();
Application.RequestStop();
}
Expand All @@ -258,7 +253,6 @@ private void RemoveMailbox()
{
if (mailboxListView.SelectedItem >= 0)
{
var serverOptions = settingsManager.GetServerOptions();
if (serverOptions.Mailboxes != null && mailboxListView.SelectedItem < serverOptions.Mailboxes.Length)
{
var mailbox = serverOptions.Mailboxes[mailboxListView.SelectedItem];
Expand All @@ -271,7 +265,6 @@ private void RemoveMailbox()
var mailboxesList = serverOptions.Mailboxes.ToList();
mailboxesList.RemoveAt(mailboxListView.SelectedItem);
serverOptions.Mailboxes = mailboxesList.ToArray();
settingsManager.SaveSettings(serverOptions, settingsManager.GetRelayOptions()).Wait();
RefreshList();
}
}
Expand Down
112 changes: 73 additions & 39 deletions Rnwood.Smtp4dev/TUI/MessagesTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class MessagesTab
{
private readonly IHost host;
private View container;
private ListView messageListView;
private TableView messageTableView;
private View detailsPanel;
private TextView overviewBodyTextView;
private TableView headersTableView;
Expand All @@ -32,7 +32,7 @@ public class MessagesTab
private List<Message> filteredMessages = new List<Message>();
private Message selectedMessage;
private string searchFilter = string.Empty;
private int lastSelectedIndex = -1;
private int lastSelectedRow = -1;

public MessagesTab(IHost host)
{
Expand All @@ -51,73 +51,93 @@ private void CreateUI()
Height = Dim.Fill()
};

// Status label and search field at top
// Status label at top
statusLabel = new Label("Loading messages...")
{
X = 0,
Y = 0,
Width = Dim.Percent(50)
Width = Dim.Fill()
};

container.Add(statusLabel);

// Left panel - Message table (40% width) - no frame
messageTableView = new TableView()
{
X = 0,
Y = 1,
Width = Dim.Percent(40),
Height = Dim.Fill() - 4,
FullRowSelect = true
};

// Setup table columns
var table = new DataTable();
table.Columns.Add("", typeof(string)); // Unread indicator
table.Columns.Add("Date", typeof(string));
table.Columns.Add("From", typeof(string));
table.Columns.Add("Subject", typeof(string));
messageTableView.Table = table;

messageTableView.SelectedCellChanged += OnMessageSelected;
messageTableView.KeyPress += (e) => {
if (e.KeyEvent.Key == Key.DeleteChar || e.KeyEvent.Key == Key.Backspace)
{
DeleteSelected();
e.Handled = true;
}
};

container.Add(messageTableView);

// Search box below the list
var searchLabel = new Label("Search:")
{
X = Pos.Right(statusLabel) + 2,
Y = 0
X = 0,
Y = Pos.Bottom(messageTableView)
};

searchField = new TextField("")
{
X = Pos.Right(searchLabel) + 1,
Y = 0,
Width = Dim.Fill() - 1
Y = Pos.Bottom(messageTableView),
Width = Dim.Fill() - 10
};
searchField.TextChanged += (old) => ApplyFilter();

container.Add(statusLabel, searchLabel, searchField);

// Left panel - Message list (40% width) - no frame
messageListView = new ListView()
{
X = 0,
Y = 1,
Width = Dim.Percent(40),
Height = Dim.Fill() - 3,
AllowsMarking = false,
CanFocus = true
};

messageListView.SelectedItemChanged += OnMessageSelected;
container.Add(searchLabel, searchField);

// Action buttons below search
var deleteButton = new Button("Delete")
{
X = 0,
Y = Pos.Bottom(messageListView),
Y = Pos.Bottom(searchField),
Width = 10
};
deleteButton.Clicked += () => DeleteSelected();

var deleteAllButton = new Button("Delete All")
{
X = Pos.Right(deleteButton) + 1,
Y = Pos.Bottom(messageListView),
Y = Pos.Bottom(searchField),
Width = 12
};
deleteAllButton.Clicked += () => DeleteAll();

var composeButton = new Button("Compose")
{
X = Pos.Right(deleteAllButton) + 1,
Y = Pos.Bottom(messageListView),
Y = Pos.Bottom(searchField),
Width = 10
};
composeButton.Clicked += () => ComposeMessage();

container.Add(messageListView, deleteButton, deleteAllButton, composeButton);
container.Add(deleteButton, deleteAllButton, composeButton);

// Right panel - Message details (60% width) - no frame
detailsPanel = new View()
{
X = Pos.Right(messageListView) + 1,
X = Pos.Right(messageTableView) + 1,
Y = 1,
Width = Dim.Fill(),
Height = Dim.Fill() - 1
Expand Down Expand Up @@ -253,7 +273,10 @@ public View GetView()
public void Refresh()
{
// Save current selection
lastSelectedIndex = messageListView.SelectedItem;
if (messageTableView.Table != null && messageTableView.SelectedRow >= 0)
{
lastSelectedRow = messageTableView.SelectedRow;
}

var dbContext = host.Services.GetRequiredService<Smtp4devDbContext>();
messages = dbContext.Messages
Expand Down Expand Up @@ -282,28 +305,39 @@ private void ApplyFilter()
).ToList();
}

var messageStrings = filteredMessages.Select(m =>
var table = new DataTable();
table.Columns.Add("", typeof(string)); // Unread indicator
table.Columns.Add("Date", typeof(string));
table.Columns.Add("From", typeof(string));
table.Columns.Add("Subject", typeof(string));

foreach (var message in filteredMessages)
{
var unreadIndicator = m.IsUnread ? "* " : " ";
return $"{unreadIndicator}{m.ReceivedDate:yyyy-MM-dd HH:mm} | {TruncateString(m.From ?? "", 20)} | {TruncateString(m.Subject ?? "", 35)}";
}).ToList();
table.Rows.Add(
message.IsUnread ? "*" : " ",
message.ReceivedDate.ToString("yyyy-MM-dd HH:mm"),
TruncateString(message.From ?? "", 20),
TruncateString(message.Subject ?? "", 35)
);
}

messageListView.SetSource(messageStrings);
messageTableView.Table = table;

statusLabel.Text = $"Messages: {filteredMessages.Count}/{messages.Count}";

// Restore selection if possible
if (lastSelectedIndex >= 0 && lastSelectedIndex < filteredMessages.Count)
if (lastSelectedRow >= 0 && lastSelectedRow < filteredMessages.Count)
{
messageListView.SelectedItem = lastSelectedIndex;
messageTableView.SelectedRow = lastSelectedRow;
}
}

private void OnMessageSelected(ListViewItemEventArgs args)
private void OnMessageSelected(TableView.SelectedCellChangedEventArgs args)
{
if (args.Item >= 0 && args.Item < filteredMessages.Count)
if (args.NewRow >= 0 && args.NewRow < filteredMessages.Count)
{
lastSelectedIndex = args.Item;
selectedMessage = filteredMessages[args.Item];
lastSelectedRow = args.NewRow;
selectedMessage = filteredMessages[args.NewRow];
ShowMessageDetails();
}
}
Expand Down
Loading