2 Commits
v1.2.0 ... v1.3

Author SHA1 Message Date
Lucas Bortoli
cb55d0148d Merge pull request #11 from alexhorner/master
Add adjustable reconnects and settings revisions
2023-12-28 12:14:50 -03:00
Alexander Horner
0155cfb8fc Add adjustable reconnects and settings revisions 2023-10-09 21:11:07 +01:00
6 changed files with 266 additions and 103 deletions

View File

@@ -93,14 +93,18 @@ namespace ntfysh_client
//Do not subscribe on cancelled dialog //Do not subscribe on cancelled dialog
if (result != DialogResult.OK) return; if (result != DialogResult.OK) return;
//Convert the reconnection values to ints
int reconnectAttempts = Convert.ToInt32(Math.Ceiling(Program.Settings.ReconnectAttempts));
int reconnectAttemptDelay = Convert.ToInt32(Math.Ceiling(Program.Settings.ReconnectAttemptDelay));
//Subscribe //Subscribe
if (dialog.UseWebsockets) if (dialog.UseWebsockets)
{ {
_notificationListener.SubscribeToTopicUsingWebsocket(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password); _notificationListener.SubscribeToTopicUsingWebsocket(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password, reconnectAttempts, reconnectAttemptDelay);
} }
else else
{ {
_notificationListener.SubscribeToTopicUsingLongHttpJson(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password); _notificationListener.SubscribeToTopicUsingLongHttpJson(dialog.Unique, dialog.TopicId, dialog.ServerUrl, dialog.Username, dialog.Password, reconnectAttempts, reconnectAttemptDelay);
} }
//Add to the user visible list //Add to the user visible list
@@ -129,6 +133,8 @@ namespace ntfysh_client
//Load current settings into dialog //Load current settings into dialog
dialog.Timeout = Program.Settings.Timeout; dialog.Timeout = Program.Settings.Timeout;
dialog.ReconnectAttempts = Program.Settings.ReconnectAttempts;
dialog.ReconnectAttemptDelay = Program.Settings.ReconnectAttemptDelay;
//Show dialog //Show dialog
DialogResult result = dialog.ShowDialog(); DialogResult result = dialog.ShowDialog();
@@ -138,6 +144,8 @@ namespace ntfysh_client
//Read new settings from dialog //Read new settings from dialog
Program.Settings.Timeout = dialog.Timeout; Program.Settings.Timeout = dialog.Timeout;
Program.Settings.ReconnectAttempts = dialog.ReconnectAttempts;
Program.Settings.ReconnectAttemptDelay = dialog.ReconnectAttemptDelay;
//Save new settings persistently //Save new settings persistently
SaveSettingsToFile(); SaveSettingsToFile();
@@ -259,6 +267,10 @@ namespace ntfysh_client
return; return;
} }
//Convert the reconnection values to ints
int reconnectAttempts = Convert.ToInt32(Math.Ceiling(Program.Settings.ReconnectAttempts));
int reconnectAttemptDelay = Convert.ToInt32(Math.Ceiling(Program.Settings.ReconnectAttemptDelay));
//Load them in //Load them in
foreach (SubscribedTopic topic in topics) foreach (SubscribedTopic topic in topics)
{ {
@@ -268,12 +280,12 @@ namespace ntfysh_client
{ {
case "ws": case "ws":
case "wss": case "wss":
_notificationListener.SubscribeToTopicUsingWebsocket($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password); _notificationListener.SubscribeToTopicUsingWebsocket($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password, reconnectAttempts, reconnectAttemptDelay);
break; break;
case "http": case "http":
case "https": case "https":
_notificationListener.SubscribeToTopicUsingLongHttpJson($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password); _notificationListener.SubscribeToTopicUsingLongHttpJson($"{topic.TopicId}@{topic.ServerUrl}", topic.TopicId, topic.ServerUrl, topic.Username, topic.Password, reconnectAttempts, reconnectAttemptDelay);
break; break;
default: default:
@@ -286,17 +298,34 @@ namespace ntfysh_client
private SettingsModel GetDefaultSettings() => new() private SettingsModel GetDefaultSettings() => new()
{ {
Timeout = 5 Revision = 1,
Timeout = 5,
ReconnectAttempts = 10,
ReconnectAttemptDelay = 3
}; };
private void MergeSettingsRevisions(SettingsModel older, SettingsModel newer)
{
//Apply settings introduced in Revision 1
if (older.Revision < 1)
{
older.ReconnectAttempts = newer.ReconnectAttempts;
older.ReconnectAttemptDelay = newer.ReconnectAttemptDelay;
}
//Update the revision
older.Revision = newer.Revision;
}
private void LoadSettings() private void LoadSettings()
{ {
string settingsFilePath = GetSettingsFilePath(); string settingsFilePath = GetSettingsFilePath();
SettingsModel defaultSettings = GetDefaultSettings();
//Check if we have any settings file on disk to load. If we don't, initialise defaults //Check if we have any settings file on disk to load. If we don't, initialise defaults
if (!File.Exists(settingsFilePath)) if (!File.Exists(settingsFilePath))
{ {
Program.Settings = GetDefaultSettings(); Program.Settings = defaultSettings;
SaveSettingsToFile(); SaveSettingsToFile();
@@ -309,7 +338,7 @@ namespace ntfysh_client
//Check if the file is empty. If it is, initialise default settings //Check if the file is empty. If it is, initialise default settings
if (string.IsNullOrWhiteSpace(settingsSerialised)) if (string.IsNullOrWhiteSpace(settingsSerialised))
{ {
Program.Settings = GetDefaultSettings(); Program.Settings = defaultSettings;
SaveSettingsToFile(); SaveSettingsToFile();
@@ -322,7 +351,7 @@ namespace ntfysh_client
//Check if the deserialise succeeded. If it didn't, initialise default settings //Check if the deserialise succeeded. If it didn't, initialise default settings
if (settings is null) if (settings is null)
{ {
Program.Settings = GetDefaultSettings(); Program.Settings = defaultSettings;
SaveSettingsToFile(); SaveSettingsToFile();
@@ -330,6 +359,13 @@ namespace ntfysh_client
} }
Program.Settings = settings; Program.Settings = settings;
//Check the settings revision. If it is older than the current latest revision, apply the settings defaults missing from previous revision
if (Program.Settings.Revision < defaultSettings.ReconnectAttempts)
{
MergeSettingsRevisions(Program.Settings, defaultSettings);
SaveSettingsToFile();
}
} }
private void MainForm_FormClosed(object sender, FormClosedEventArgs e) private void MainForm_FormClosed(object sender, FormClosedEventArgs e)

View File

@@ -31,16 +31,16 @@ namespace ntfysh_client.Notifications
ServicePointManager.DefaultConnectionLimit = 100; ServicePointManager.DefaultConnectionLimit = 100;
} }
private async Task ListenToTopicWithHttpLongJsonAsync(HttpRequestMessage message, CancellationToken cancellationToken, SubscribedTopic topic) private async Task ListenToTopicWithHttpLongJsonAsync(HttpRequestMessage message, SubscribedTopic topic, int reconnectAttempts = 10, int reconnectAttemptDelay = 3, CancellationToken cancellationToken = default)
{ {
int connectionAttempts = 0; int connectionAttempts = 0;
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
//See if we have exceeded maximum attempts //See if we have exceeded maximum attempts
if (connectionAttempts >= 10) if (reconnectAttempts != 0 && connectionAttempts >= reconnectAttempts)
{ {
//10 connection failures (1 initial + 9 reattempts)! Do not retry //<reconnectAttempts> connection failures (1 initial + (<reconnectAttempts> - 1) reattempts)! Do not retry
OnConnectionMultiAttemptFailure?.Invoke(this, topic); OnConnectionMultiAttemptFailure?.Invoke(this, topic);
return; return;
} }
@@ -119,16 +119,16 @@ namespace ntfysh_client.Notifications
} }
finally finally
{ {
//We land here if we fail to connect or our connection gets closed (and if we are canceeling, but that gets ignored) //We land here if we fail to connect or our connection gets closed (and if we are canceling, but that gets ignored)
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
{ {
//Not cancelling, legitimate connection failure or termination //Not cancelling, legitimate connection failure or termination
if (connectionAttempts != 0) if (reconnectAttempts == 0 || connectionAttempts != 0)
{ {
//On our first reconnect attempt, try instantly. On consecutive, wait 3 seconds before each attempt //On our first reconnect attempt, try instantly (unless we have infinite retries). On consecutive, wait <reconnectAttemptDelay> seconds before each attempt
await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); await Task.Delay(TimeSpan.FromSeconds(reconnectAttemptDelay), cancellationToken);
} }
//Increment attempts //Increment attempts
@@ -140,16 +140,16 @@ namespace ntfysh_client.Notifications
} }
} }
private async Task ListenToTopicWithWebsocketAsync(Uri uri, string? credentials, CancellationToken cancellationToken, SubscribedTopic topic) private async Task ListenToTopicWithWebsocketAsync(Uri uri, string? credentials, SubscribedTopic topic, int reconnectAttempts = 10, int reconnectAttemptDelay = 3, CancellationToken cancellationToken = default)
{ {
int connectionAttempts = 0; int connectionAttempts = 0;
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
//See if we have exceeded maximum attempts //See if we have exceeded maximum attempts
if (connectionAttempts >= 10) if (reconnectAttempts != 0 && connectionAttempts >= reconnectAttempts)
{ {
//10 connection failures (1 initial + 9 reattempts)! Do not retry //<reconnectAttempts> connection failures (1 initial + (<reconnectAttempts> - 1) reattempts)! Do not retry
OnConnectionMultiAttemptFailure?.Invoke(this, topic); OnConnectionMultiAttemptFailure?.Invoke(this, topic);
return; return;
} }
@@ -223,16 +223,16 @@ namespace ntfysh_client.Notifications
} }
finally finally
{ {
//We land here if we fail to connect or our connection gets closed (and if we are canceeling, but that gets ignored) //We land here if we fail to connect or our connection gets closed (and if we are canceling, but that gets ignored)
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
{ {
//Not cancelling, legitimate connection failure or termination //Not cancelling, legitimate connection failure or termination
if (connectionAttempts != 0) if (reconnectAttempts == 0 || connectionAttempts != 0)
{ {
//On our first reconnect attempt, try instantly. On consecutive, wait 3 seconds before each attempt //On our first reconnect attempt, try instantly (unless we have infinite retries). On consecutive, wait <reconnectAttemptDelay> seconds before each attempt
await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); await Task.Delay(TimeSpan.FromSeconds(reconnectAttemptDelay), cancellationToken);
} }
//Increment attempts //Increment attempts
@@ -261,13 +261,16 @@ namespace ntfysh_client.Notifications
} }
} }
public void SubscribeToTopicUsingLongHttpJson(string unique, string topicId, string serverUrl, string? username, string? password) public void SubscribeToTopicUsingLongHttpJson(string unique, string topicId, string serverUrl, string? username, string? password, int reconnectAttempts, int reconnectAttemptDelay)
{ {
if (SubscribedTopicsByUnique.ContainsKey(unique)) throw new InvalidOperationException("A topic with this unique already exists"); if (SubscribedTopicsByUnique.ContainsKey(unique)) throw new ArgumentException("A topic with this unique already exists", nameof(unique));
if (string.IsNullOrWhiteSpace(username)) username = null; if (string.IsNullOrWhiteSpace(username)) username = null;
if (string.IsNullOrWhiteSpace(password)) password = null; if (string.IsNullOrWhiteSpace(password)) password = null;
if (reconnectAttempts < 0) throw new ArgumentException("Reconnect attempts must be 0 or more", nameof(reconnectAttempts));
if (reconnectAttemptDelay < 0) throw new ArgumentException("Reconnect attempt delay; must be 0 or more", nameof(reconnectAttemptDelay));
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, $"{serverUrl}/{HttpUtility.UrlEncode(topicId)}/json"); HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, $"{serverUrl}/{HttpUtility.UrlEncode(topicId)}/json");
if (username is not null && password is not null) if (username is not null && password is not null)
@@ -280,20 +283,23 @@ namespace ntfysh_client.Notifications
SubscribedTopic newTopic = new(topicId, serverUrl, username, password); SubscribedTopic newTopic = new(topicId, serverUrl, username, password);
CancellationTokenSource listenCanceller = new(); CancellationTokenSource listenCanceller = new();
Task listenTask = ListenToTopicWithHttpLongJsonAsync(message, listenCanceller.Token, newTopic); Task listenTask = ListenToTopicWithHttpLongJsonAsync(message, newTopic, reconnectAttempts, reconnectAttemptDelay, listenCanceller.Token);
newTopic.SetAssociatedRunner(listenTask, listenCanceller); newTopic.SetAssociatedRunner(listenTask, listenCanceller);
SubscribedTopicsByUnique.Add(unique, newTopic); SubscribedTopicsByUnique.Add(unique, newTopic);
} }
public void SubscribeToTopicUsingWebsocket(string unique, string topicId, string serverUrl, string? username, string? password) public void SubscribeToTopicUsingWebsocket(string unique, string topicId, string serverUrl, string? username, string? password, int reconnectAttempts, int reconnectAttemptDelay)
{ {
if (SubscribedTopicsByUnique.ContainsKey(unique)) throw new InvalidOperationException("A topic with this unique already exists"); if (SubscribedTopicsByUnique.ContainsKey(unique)) throw new ArgumentException("A topic with this unique already exists", nameof(unique));
if (string.IsNullOrWhiteSpace(username)) username = null; if (string.IsNullOrWhiteSpace(username)) username = null;
if (string.IsNullOrWhiteSpace(password)) password = null; if (string.IsNullOrWhiteSpace(password)) password = null;
if (reconnectAttempts < 0) throw new ArgumentException("Reconnect attempts must be 0 or more", nameof(reconnectAttempts));
if (reconnectAttemptDelay < 0) throw new ArgumentException("Reconnect attempt delay; must be 0 or more", nameof(reconnectAttemptDelay));
SubscribedTopic newTopic = new(topicId, serverUrl, username, password); SubscribedTopic newTopic = new(topicId, serverUrl, username, password);
string? credentials = null; string? credentials = null;
@@ -306,7 +312,7 @@ namespace ntfysh_client.Notifications
} }
CancellationTokenSource listenCanceller = new(); CancellationTokenSource listenCanceller = new();
Task listenTask = ListenToTopicWithWebsocketAsync(new Uri($"{serverUrl}/{HttpUtility.UrlEncode(topicId)}/ws"), credentials, listenCanceller.Token, newTopic); Task listenTask = ListenToTopicWithWebsocketAsync(new Uri($"{serverUrl}/{HttpUtility.UrlEncode(topicId)}/ws"), credentials, newTopic, reconnectAttempts, reconnectAttemptDelay, listenCanceller.Token);
newTopic.SetAssociatedRunner(listenTask, listenCanceller); newTopic.SetAssociatedRunner(listenTask, listenCanceller);

View File

@@ -29,101 +29,147 @@ namespace ntfysh_client
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.panel1 = new System.Windows.Forms.Panel(); buttonPanel = new System.Windows.Forms.Panel();
this.cancelButton = new System.Windows.Forms.Button(); cancelButton = new System.Windows.Forms.Button();
this.saveButton = new System.Windows.Forms.Button(); saveButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label(); timeoutLabel = new System.Windows.Forms.Label();
this.timeout = new System.Windows.Forms.NumericUpDown(); timeout = new System.Windows.Forms.NumericUpDown();
this.panel1.SuspendLayout(); reconnectAttempts = new System.Windows.Forms.NumericUpDown();
((System.ComponentModel.ISupportInitialize)(this.timeout)).BeginInit(); reconnectAttemptsLabel = new System.Windows.Forms.Label();
this.SuspendLayout(); reconnectAttemptDelay = new System.Windows.Forms.NumericUpDown();
reconnectAttemptDelayLabel = new System.Windows.Forms.Label();
buttonPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)timeout).BeginInit();
((System.ComponentModel.ISupportInitialize)reconnectAttempts).BeginInit();
((System.ComponentModel.ISupportInitialize)reconnectAttemptDelay).BeginInit();
SuspendLayout();
// //
// panel1 // buttonPanel
// //
this.panel1.BackColor = System.Drawing.SystemColors.Control; buttonPanel.BackColor = System.Drawing.SystemColors.Control;
this.panel1.Controls.Add(this.cancelButton); buttonPanel.Controls.Add(cancelButton);
this.panel1.Controls.Add(this.saveButton); buttonPanel.Controls.Add(saveButton);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; buttonPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 61); buttonPanel.Location = new System.Drawing.Point(0, 150);
this.panel1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); buttonPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.panel1.Name = "panel1"; buttonPanel.Name = "buttonPanel";
this.panel1.Size = new System.Drawing.Size(531, 51); buttonPanel.Size = new System.Drawing.Size(531, 51);
this.panel1.TabIndex = 9; buttonPanel.TabIndex = 0;
// //
// cancelButton // cancelButton
// //
this.cancelButton.Location = new System.Drawing.Point(363, 16); cancelButton.Location = new System.Drawing.Point(363, 16);
this.cancelButton.Name = "cancelButton"; cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23); cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 1; cancelButton.TabIndex = 2;
this.cancelButton.Text = "Cancel"; cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true; cancelButton.UseVisualStyleBackColor = true;
this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); cancelButton.Click += cancelButton_Click;
// //
// saveButton // saveButton
// //
this.saveButton.Location = new System.Drawing.Point(444, 16); saveButton.Location = new System.Drawing.Point(444, 16);
this.saveButton.Name = "saveButton"; saveButton.Name = "saveButton";
this.saveButton.Size = new System.Drawing.Size(75, 23); saveButton.Size = new System.Drawing.Size(75, 23);
this.saveButton.TabIndex = 0; saveButton.TabIndex = 1;
this.saveButton.Text = "Save"; saveButton.Text = "Save";
this.saveButton.UseVisualStyleBackColor = true; saveButton.UseVisualStyleBackColor = true;
this.saveButton.Click += new System.EventHandler(this.saveButton_Click); saveButton.Click += saveButton_Click;
// //
// label1 // timeoutLabel
// //
this.label1.AutoSize = true; timeoutLabel.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 9); timeoutLabel.Location = new System.Drawing.Point(13, 9);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); timeoutLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1"; timeoutLabel.Name = "timeoutLabel";
this.label1.Size = new System.Drawing.Size(488, 15); timeoutLabel.Size = new System.Drawing.Size(488, 15);
this.label1.TabIndex = 11; timeoutLabel.TabIndex = 3;
this.label1.Text = "Notification Toast Timeout (seconds, may be ignored by OS based on accessibility " + timeoutLabel.Text = "Notification Toast Timeout (seconds, may be ignored by OS based on accessibility settings):";
"settings):";
// //
// timeout // timeout
// //
this.timeout.Location = new System.Drawing.Point(13, 27); timeout.Location = new System.Drawing.Point(13, 28);
this.timeout.Maximum = new decimal(new int[] { timeout.Maximum = new decimal(new int[] { -1981284353, -1966660860, 0, 0 });
-1981284353, timeout.Name = "timeout";
-1966660860, timeout.Size = new System.Drawing.Size(506, 23);
0, timeout.TabIndex = 4;
0}); //
this.timeout.Name = "timeout"; // reconnectAttempts
this.timeout.Size = new System.Drawing.Size(506, 23); //
this.timeout.TabIndex = 12; reconnectAttempts.Location = new System.Drawing.Point(12, 73);
reconnectAttempts.Maximum = new decimal(new int[] { -1981284353, -1966660860, 0, 0 });
reconnectAttempts.Name = "reconnectAttempts";
reconnectAttempts.Size = new System.Drawing.Size(506, 23);
reconnectAttempts.TabIndex = 6;
//
// reconnectAttemptsLabel
//
reconnectAttemptsLabel.AutoSize = true;
reconnectAttemptsLabel.Location = new System.Drawing.Point(12, 54);
reconnectAttemptsLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
reconnectAttemptsLabel.Name = "reconnectAttemptsLabel";
reconnectAttemptsLabel.Size = new System.Drawing.Size(198, 15);
reconnectAttemptsLabel.TabIndex = 5;
reconnectAttemptsLabel.Text = "Maximum reconnect retry attempts (requires restart):";
//
// reconnectAttemptDelay
//
reconnectAttemptDelay.Location = new System.Drawing.Point(12, 118);
reconnectAttemptDelay.Maximum = new decimal(new int[] { -1981284353, -1966660860, 0, 0 });
reconnectAttemptDelay.Name = "reconnectAttemptDelay";
reconnectAttemptDelay.Size = new System.Drawing.Size(506, 23);
reconnectAttemptDelay.TabIndex = 8;
//
// reconnectAttemptDelayLabel
//
reconnectAttemptDelayLabel.AutoSize = true;
reconnectAttemptDelayLabel.Location = new System.Drawing.Point(12, 99);
reconnectAttemptDelayLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
reconnectAttemptDelayLabel.Name = "reconnectAttemptDelayLabel";
reconnectAttemptDelayLabel.Size = new System.Drawing.Size(191, 15);
reconnectAttemptDelayLabel.TabIndex = 7;
reconnectAttemptDelayLabel.Text = "Delay between attempts (seconds, requires restart):";
// //
// SettingsDialog // SettingsDialog
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White; BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(531, 112); ClientSize = new System.Drawing.Size(531, 201);
this.Controls.Add(this.timeout); Controls.Add(reconnectAttemptDelay);
this.Controls.Add(this.label1); Controls.Add(reconnectAttemptDelayLabel);
this.Controls.Add(this.panel1); Controls.Add(reconnectAttempts);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; Controls.Add(reconnectAttemptsLabel);
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); Controls.Add(timeout);
this.MaximizeBox = false; Controls.Add(timeoutLabel);
this.MinimizeBox = false; Controls.Add(buttonPanel);
this.Name = "SettingsDialog"; FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.ShowIcon = false; Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.ShowInTaskbar = false; MaximizeBox = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; MinimizeBox = false;
this.Text = "Settings"; Name = "SettingsDialog";
this.panel1.ResumeLayout(false); ShowIcon = false;
((System.ComponentModel.ISupportInitialize)(this.timeout)).EndInit(); ShowInTaskbar = false;
this.ResumeLayout(false); StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.PerformLayout(); Text = "Settings";
buttonPanel.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)timeout).EndInit();
((System.ComponentModel.ISupportInitialize)reconnectAttempts).EndInit();
((System.ComponentModel.ISupportInitialize)reconnectAttemptDelay).EndInit();
ResumeLayout(false);
PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel buttonPanel;
private System.Windows.Forms.Label label1; private System.Windows.Forms.Label timeoutLabel;
private System.Windows.Forms.NumericUpDown timeout; private System.Windows.Forms.NumericUpDown timeout;
private System.Windows.Forms.Button cancelButton; private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button saveButton; private System.Windows.Forms.Button saveButton;
private System.Windows.Forms.NumericUpDown reconnectAttempts;
private System.Windows.Forms.Label reconnectAttemptsLabel;
private System.Windows.Forms.NumericUpDown reconnectAttemptDelay;
private System.Windows.Forms.Label reconnectAttemptDelayLabel;
} }
} }

View File

@@ -11,6 +11,18 @@ namespace ntfysh_client
set => timeout.Value = value; set => timeout.Value = value;
} }
public decimal ReconnectAttempts
{
get => reconnectAttempts.Value;
set => reconnectAttempts.Value = value;
}
public decimal ReconnectAttemptDelay
{
get => reconnectAttemptDelay.Value;
set => reconnectAttemptDelay.Value = value;
}
public SettingsDialog() public SettingsDialog()
{ {
InitializeComponent(); InitializeComponent();

View File

@@ -1,4 +1,64 @@
<root> <?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true"> <xsd:element name="root" msdata:IsDataSet="true">

View File

@@ -2,6 +2,9 @@
{ {
public class SettingsModel public class SettingsModel
{ {
public uint Revision { get; set; }
public decimal Timeout { get; set; } public decimal Timeout { get; set; }
public decimal ReconnectAttempts { get; set; }
public decimal ReconnectAttemptDelay { get; set; }
} }
} }