M5: Windows client moderation UI; add C ABI getters for account list, user mute/deafen, channel topic
This commit is contained in:
200
clients/windows/VoiceCat.App/Forms/AccountsDialog.cs
Normal file
200
clients/windows/VoiceCat.App/Forms/AccountsDialog.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using VoiceCat.Interop;
|
||||
|
||||
namespace VoiceCat.App.Forms;
|
||||
|
||||
/// <summary>
|
||||
/// Admin account management: list, create, reset password, and delete server accounts.
|
||||
/// </summary>
|
||||
public sealed class AccountsDialog : Form
|
||||
{
|
||||
private readonly VoiceCatClient _client;
|
||||
private readonly ListView _lvAccounts;
|
||||
private readonly Button _btnRefresh;
|
||||
private readonly Button _btnAdd;
|
||||
private readonly Button _btnResetPassword;
|
||||
private readonly Button _btnDelete;
|
||||
|
||||
public AccountsDialog(VoiceCatClient client)
|
||||
{
|
||||
_client = client;
|
||||
|
||||
Text = "Server accounts";
|
||||
FormBorderStyle = FormBorderStyle.Sizable;
|
||||
StartPosition = FormStartPosition.CenterParent;
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(560, 360);
|
||||
MinimumSize = new Size(420, 260);
|
||||
|
||||
_lvAccounts = new ListView
|
||||
{
|
||||
View = View.Details,
|
||||
FullRowSelect = true,
|
||||
GridLines = true,
|
||||
Dock = DockStyle.Fill,
|
||||
TabIndex = 0,
|
||||
};
|
||||
_lvAccounts.Columns.Add("Username", 160);
|
||||
_lvAccounts.Columns.Add("Admin", 50);
|
||||
_lvAccounts.Columns.Add("Created", 130);
|
||||
_lvAccounts.Columns.Add("Last login", 130);
|
||||
_lvAccounts.SelectedIndexChanged += (_, _) => UpdateButtons();
|
||||
|
||||
var pnlButtons = new Panel
|
||||
{
|
||||
Dock = DockStyle.Bottom,
|
||||
Height = 44,
|
||||
};
|
||||
|
||||
_btnRefresh = new Button
|
||||
{
|
||||
Text = "&Refresh",
|
||||
Location = new Point(12, 9),
|
||||
Size = new Size(75, 27),
|
||||
TabIndex = 1,
|
||||
};
|
||||
_btnRefresh.Click += (_, _) => RefreshList();
|
||||
|
||||
_btnAdd = new Button
|
||||
{
|
||||
Text = "&Add...",
|
||||
Location = new Point(100, 9),
|
||||
Size = new Size(75, 27),
|
||||
TabIndex = 2,
|
||||
};
|
||||
_btnAdd.Click += (_, _) => AddAccount();
|
||||
|
||||
_btnResetPassword = new Button
|
||||
{
|
||||
Text = "&Reset password...",
|
||||
Location = new Point(188, 9),
|
||||
Size = new Size(120, 27),
|
||||
TabIndex = 3,
|
||||
Enabled = false,
|
||||
};
|
||||
_btnResetPassword.Click += (_, _) => ResetPassword();
|
||||
|
||||
_btnDelete = new Button
|
||||
{
|
||||
Text = "&Delete",
|
||||
Location = new Point(320, 9),
|
||||
Size = new Size(75, 27),
|
||||
TabIndex = 4,
|
||||
Enabled = false,
|
||||
};
|
||||
_btnDelete.Click += (_, _) => DeleteAccount();
|
||||
|
||||
pnlButtons.Controls.AddRange([_btnRefresh, _btnAdd, _btnResetPassword, _btnDelete]);
|
||||
|
||||
Controls.Add(_lvAccounts);
|
||||
Controls.Add(pnlButtons);
|
||||
|
||||
Load += (_, _) => RefreshList();
|
||||
}
|
||||
|
||||
private void UpdateButtons()
|
||||
{
|
||||
bool selected = _lvAccounts.SelectedItems.Count > 0;
|
||||
_btnResetPassword.Enabled = selected;
|
||||
_btnDelete.Enabled = selected;
|
||||
}
|
||||
|
||||
private void RefreshList()
|
||||
{
|
||||
_client.RequestAccountList();
|
||||
// The event will come back asynchronously; poll briefly for the list.
|
||||
var deadline = DateTime.UtcNow.AddSeconds(2);
|
||||
List<AccountInfo>? accounts = null;
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
_client.PumpEvents();
|
||||
accounts = _client.ListAccounts();
|
||||
if (accounts.Count > 0) break;
|
||||
Thread.Sleep(30);
|
||||
}
|
||||
accounts ??= _client.ListAccounts();
|
||||
|
||||
_lvAccounts.BeginUpdate();
|
||||
_lvAccounts.Items.Clear();
|
||||
foreach (var a in accounts.OrderBy(a => a.Username))
|
||||
{
|
||||
var item = new ListViewItem(a.Username);
|
||||
item.SubItems.Add(a.IsAdmin ? "Yes" : "No");
|
||||
item.SubItems.Add(FormatDate(a.CreatedAtUnixMs));
|
||||
item.SubItems.Add(FormatDate(a.LastLoginUnixMs));
|
||||
_lvAccounts.Items.Add(item);
|
||||
}
|
||||
_lvAccounts.EndUpdate();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private static string FormatDate(ulong ms)
|
||||
{
|
||||
if (ms == 0) return "—";
|
||||
try
|
||||
{
|
||||
return DateTimeOffset.FromUnixTimeMilliseconds((long)ms).LocalDateTime.ToString("g");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "—";
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAccount()
|
||||
{
|
||||
using var userDlg = new InputDialog("Add account", "&Username:");
|
||||
if (userDlg.ShowDialog(this) != DialogResult.OK || string.IsNullOrWhiteSpace(userDlg.TextValue))
|
||||
return;
|
||||
|
||||
using var pwDlg = new PasswordPromptDialog($"Password for {userDlg.TextValue}:");
|
||||
if (pwDlg.ShowDialog(this) != DialogResult.OK || string.IsNullOrEmpty(pwDlg.Password))
|
||||
return;
|
||||
|
||||
var r = _client.CreateAccount(userDlg.TextValue, pwDlg.Password);
|
||||
if (r != VcResult.Ok)
|
||||
{
|
||||
MessageBox.Show(this, $"Create account failed: {r}", "VoiceCat",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private void ResetPassword()
|
||||
{
|
||||
if (_lvAccounts.SelectedItems.Count == 0) return;
|
||||
string username = _lvAccounts.SelectedItems[0].Text;
|
||||
|
||||
using var dlg = new PasswordPromptDialog($"New password for {username}:");
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK || string.IsNullOrEmpty(dlg.Password))
|
||||
return;
|
||||
|
||||
var r = _client.ResetPassword(username, dlg.Password);
|
||||
if (r != VcResult.Ok)
|
||||
{
|
||||
MessageBox.Show(this, $"Reset password failed: {r}", "VoiceCat",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private void DeleteAccount()
|
||||
{
|
||||
if (_lvAccounts.SelectedItems.Count == 0) return;
|
||||
string username = _lvAccounts.SelectedItems[0].Text;
|
||||
|
||||
var confirm = MessageBox.Show(this, $"Delete account '{username}'?",
|
||||
"VoiceCat", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (confirm != DialogResult.Yes) return;
|
||||
|
||||
var r = _client.DeleteAccount(username);
|
||||
if (r != VcResult.Ok)
|
||||
{
|
||||
MessageBox.Show(this, $"Delete account failed: {r}", "VoiceCat",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
RefreshList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user