zcbot/windows-node/Zcbot.WindowsNode/TrayApplicationContext.cs

187 lines
5.6 KiB
C#

namespace Zcbot.WindowsNode;
internal sealed class TrayApplicationContext : ApplicationContext
{
private readonly NodeConfigStore store;
private readonly ConfigurationForm form;
private readonly NotifyIcon tray;
private readonly ToolStripMenuItem statusItem;
private CancellationTokenSource? connectionStop;
private Task? connectionTask;
private NodeConfig? config;
private NodeStatus current = NodeStatus.Create(NodeState.NotRegistered, "尚未注册");
internal TrayApplicationContext(NodeConfigStore store)
{
this.store = store;
form = new ConfigurationForm();
form.RegisterRequested += RegisterAsync;
form.ReconnectRequested += RestartConnection;
form.ResetIdentityRequested += ResetIdentity;
statusItem = new ToolStripMenuItem("尚未注册") { Enabled = false };
var menu = new ContextMenuStrip();
menu.Items.Add(statusItem);
menu.Items.Add(new ToolStripSeparator());
menu.Items.Add("打开配置", null, (_, _) => ShowConfiguration());
menu.Items.Add("立即重连", null, (_, _) => RestartConnection());
menu.Items.Add(new ToolStripSeparator());
menu.Items.Add("退出", null, (_, _) => ExitNode());
tray = new NotifyIcon
{
ContextMenuStrip = menu,
Icon = TrayIconFactory.Create(NodeState.NotRegistered),
Text = "zcbot Windows Node - 尚未注册",
Visible = true,
};
tray.DoubleClick += (_, _) => ShowConfiguration();
if (store.Exists)
{
try
{
config = store.Load();
StartConnection();
}
catch (NodeConfigurationException exception)
{
UpdateStatus(NodeStatus.Create(NodeState.AuthenticationRequired, exception.Message));
ShowConfiguration();
}
}
else
{
UpdateStatus(current);
ShowConfiguration();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
connectionStop?.Cancel();
tray.Visible = false;
tray.Dispose();
form.Dispose();
connectionStop?.Dispose();
}
base.Dispose(disposing);
}
private async Task RegisterAsync(EnrollOptions options)
{
UpdateStatus(NodeStatus.Create(NodeState.Connecting, "正在注册节点…"));
try
{
await EnrollmentClient.EnrollAsync(options, store, CancellationToken.None);
config = store.Load();
UpdateStatus(NodeStatus.Create(NodeState.Connecting, "注册成功,正在连接…"));
StartConnection();
}
catch
{
UpdateStatus(NodeStatus.Create(NodeState.NotRegistered, "注册失败,请检查配置"));
throw;
}
}
private void StartConnection()
{
if (config is null || connectionTask is { IsCompleted: false })
{
return;
}
connectionStop?.Dispose();
connectionStop = new CancellationTokenSource();
connectionTask = RunConnectionAsync(config, connectionStop.Token);
}
private async Task RunConnectionAsync(NodeConfig nodeConfig, CancellationToken cancellationToken)
{
try
{
await new NodeConnectionLoop(nodeConfig, status => PostStatus(status))
.RunAsync(cancellationToken);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
PostStatus(NodeStatus.Create(NodeState.Stopped, "连接已停止"));
}
catch (NodeConfigurationException exception)
{
PostStatus(NodeStatus.Create(NodeState.AuthenticationRequired, exception.Message));
}
catch (Exception exception)
{
PostStatus(NodeStatus.Create(NodeState.Offline, $"节点异常:{exception.Message}"));
}
}
private void RestartConnection()
{
if (config is null)
{
ShowConfiguration();
return;
}
connectionStop?.Cancel();
_ = RestartAfterStopAsync();
}
private void ResetIdentity()
{
connectionStop?.Cancel();
store.DeleteLocalIdentity();
config = null;
UpdateStatus(NodeStatus.Create(NodeState.NotRegistered, "本机身份已清除,请使用新注册码注册"));
}
private async Task RestartAfterStopAsync()
{
if (connectionTask is not null)
{
await connectionTask;
}
StartConnection();
}
private void PostStatus(NodeStatus status)
{
if (form.IsHandleCreated)
{
form.BeginInvoke(() => UpdateStatus(status));
}
}
private void UpdateStatus(NodeStatus status)
{
current = status;
form.ApplyStatus(status, config);
statusItem.Text = status.Message;
tray.Text = Limit($"zcbot Windows Node - {status.Message}", 63);
var oldIcon = tray.Icon;
tray.Icon = TrayIconFactory.Create(status.State);
oldIcon?.Dispose();
}
private void ShowConfiguration()
{
form.ApplyStatus(current, config);
form.Show();
form.WindowState = FormWindowState.Normal;
form.Activate();
}
private void ExitNode()
{
connectionStop?.Cancel();
tray.Visible = false;
ExitThread();
}
private static string Limit(string value, int length) =>
value.Length <= length ? value : value[..length];
}