Skip to content

Commit 75790f0

Browse files
committed
Added Test History and IP lookup (v1.2.0)
1 parent 8cf4bc2 commit 75790f0

File tree

8 files changed

+270
-57
lines changed

8 files changed

+270
-57
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
- Fixed auto invocation on change of theme
1+
- Added Past Test History and IP Lookup

Flow.Launcher.Plugin.SpeedTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
99
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
1010
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
11+
<UseWPF>true</UseWPF>
1112
</PropertyGroup>
1213

1314
<ItemGroup>

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ This plugin uses the Ookla Speedtest CLI to run network tests. Thanks to Ookla f
4242

4343
## Contributing 🤝
4444

45-
- To build and test locally: `dotnet build -c Release` and `.
46-
\build.ps1 -Install` (installs to your local Flow Launcher plugin path).
45+
- To build and test locally: `.\build.ps1 -Install` (installs to your local Flow Launcher plugin path).
4746
- Pull requests, issues, and improvements are welcome — please follow standard GitHub flow.
4847

4948
---

plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"Name": "Speed Test",
55
"Description": "Test your internet connection speed",
66
"Author": "swopnil7",
7-
"Version": "1.1.1",
7+
"Version": "1.2.0",
88
"Language": "csharp",
99
"Website": "https://github.com/swopnil7/Flow.Launcher.Plugin.SpeedTest",
1010
"IcoPath": "icon-dark.png",

src/Main.cs

Lines changed: 202 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Diagnostics;
5+
using System.Linq;
56
using System.Threading;
67
using System.Threading.Tasks;
78
using System.Windows;
89

910
namespace Flow.Launcher.Plugin.SpeedTest
1011
{
11-
public class Main : IAsyncPlugin, IPluginI18n
12+
public class Main : IAsyncPlugin, IPluginI18n, ISettingProvider
1213
{
1314
private PluginInitContext? _context;
1415
private Settings? _settings;
@@ -20,40 +21,36 @@ public class Main : IAsyncPlugin, IPluginI18n
2021
private double _uploadProgress;
2122
private double _currentDownloadSpeed;
2223
private double _currentUploadSpeed;
23-
private Timer? _updateTimer;
2424
private string? _lastError;
2525
private DateTime _lastQueryTime;
2626
private bool _isDarkTheme;
27+
private string _currentQuery = string.Empty;
28+
private Timer? _refreshTimer;
2729

2830
public Task InitAsync(PluginInitContext context)
2931
{
3032
_context = context;
3133
_settings = context.API.LoadSettingJsonStorage<Settings>();
3234

33-
var dispatcher = Application.Current?.Dispatcher;
34-
if (dispatcher != null)
35-
{
36-
if (dispatcher.CheckAccess())
37-
_isDarkTheme = context.API.IsApplicationDarkTheme();
38-
else
39-
dispatcher.Invoke(() => _isDarkTheme = context.API.IsApplicationDarkTheme());
40-
}
41-
35+
UpdateIcon();
4236
context.API.ActualApplicationThemeChanged += (_, __) =>
4337
{
44-
var disp = Application.Current?.Dispatcher;
45-
if (disp != null)
46-
{
47-
if (disp.CheckAccess())
48-
_isDarkTheme = _context.API.IsApplicationDarkTheme();
49-
else
50-
disp.Invoke(() => _isDarkTheme = _context.API.IsApplicationDarkTheme());
51-
}
38+
UpdateIcon();
39+
try { _context?.API.ChangeQuery(_context.CurrentPluginMetadata.ActionKeyword, true); } catch { }
5240
};
5341

5442
return Task.CompletedTask;
5543
}
5644

45+
private void UpdateIcon()
46+
{
47+
var dispatcher = Application.Current?.Dispatcher;
48+
if (dispatcher?.CheckAccess() == true)
49+
_isDarkTheme = _context.API.IsApplicationDarkTheme();
50+
else
51+
dispatcher?.Invoke(() => _isDarkTheme = _context.API.IsApplicationDarkTheme());
52+
}
53+
5754
private string GetIcon() => _isDarkTheme ? "icon-dark.png" : "icon-light.png";
5855

5956
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
@@ -68,18 +65,116 @@ public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
6865
}
6966
_lastQueryTime = DateTime.Now;
7067

71-
if (string.IsNullOrWhiteSpace(query.Search) && !_isTestRunning && _lastResult == null && _lastError == null)
68+
var q = (query.Search ?? string.Empty).Trim();
69+
70+
if (string.IsNullOrWhiteSpace(q))
7271
{
73-
_currentStatus = "Connecting to server...";
74-
RunTest();
72+
// don't auto-run tests: show usage and last result
73+
if (_lastResult != null)
74+
{
75+
var timeSince = DateTime.Now - _lastTestTime;
76+
var timeStr = timeSince.TotalMinutes < 60 ? $"{(int)timeSince.TotalMinutes}m ago" : $"{(int)timeSince.TotalHours}h ago";
77+
results.Add(new Result
78+
{
79+
Title = $"↓ {_lastResult.DownloadSpeed:F1} Mbps ↑ {_lastResult.UploadSpeed:F1} Mbps",
80+
SubTitle = $"{timeStr} • start • history • ip",
81+
IcoPath = GetIcon()
82+
});
83+
}
84+
else
85+
{
86+
results.Add(new Result
87+
{
88+
Title = "Type start to test your speed",
89+
SubTitle = "start • history • ip",
90+
IcoPath = GetIcon()
91+
});
92+
}
7593

76-
results.Add(new Result
94+
return results;
95+
}
96+
97+
// command handling
98+
var cmd = q.ToLowerInvariant();
99+
if (cmd == "start")
100+
{
101+
if (!_isTestRunning)
77102
{
78-
Title = "Testing your internet speed...",
79-
SubTitle = "Connecting to nearest server...",
80-
IcoPath = GetIcon()
81-
});
103+
_currentQuery = query.RawQuery;
104+
_currentStatus = "Connecting to server...";
105+
RunTest();
106+
107+
// Set up auto-refresh timer to update progress
108+
_refreshTimer?.Dispose();
109+
_refreshTimer = new Timer(_ =>
110+
{
111+
if (_isTestRunning && _context != null)
112+
{
113+
try
114+
{
115+
// Trigger refresh by changing query to current query
116+
_context.API.ChangeQuery(_currentQuery, true);
117+
}
118+
catch { }
119+
}
120+
}, null, 300, 300);
121+
122+
results.Add(new Result { Title = "Testing your internet speed...", SubTitle = "Connecting to nearest server...", IcoPath = GetIcon() });
123+
return results;
124+
}
125+
// if test is running, fall through to show progress
126+
}
127+
128+
if (cmd == "history")
129+
{
130+
var hist = _settings?.History ?? new List<HistoryEntry>();
131+
if (hist.Count == 0)
132+
{
133+
results.Add(new Result { Title = "No history", SubTitle = "No previous tests recorded", IcoPath = GetIcon() });
134+
return results;
135+
}
136+
137+
// show all history entries
138+
foreach (var entry in hist.AsReadOnly().Reverse())
139+
{
140+
results.Add(new Result
141+
{
142+
Title = $"↓ {entry.DownloadSpeed:F1} Mbps ↑ {entry.UploadSpeed:F1} Mbps",
143+
SubTitle = $"{entry.Time:g} • Ping: {entry.Ping:F0} ms",
144+
IcoPath = GetIcon()
145+
});
146+
}
147+
148+
return results;
149+
}
82150

151+
if (cmd == "ip")
152+
{
153+
var internalIp = await GetInternalIpAsync();
154+
results.Add(new Result
155+
{
156+
Title = "Internal IP",
157+
SubTitle = $"{internalIp} • Press Enter to copy",
158+
IcoPath = GetIcon(),
159+
Action = _ =>
160+
{
161+
try { System.Windows.Clipboard.SetText(internalIp); } catch { }
162+
return true;
163+
}
164+
});
165+
166+
var externalIp = await GetExternalIpAsync();
167+
results.Add(new Result
168+
{
169+
Title = "External IP",
170+
SubTitle = $"{externalIp} • Press Enter to copy",
171+
IcoPath = GetIcon(),
172+
Action = _ =>
173+
{
174+
try { System.Windows.Clipboard.SetText(externalIp); } catch { }
175+
return true;
176+
}
177+
});
83178
return results;
84179
}
85180

@@ -94,21 +189,16 @@ public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
94189
}
95190
else if (_lastResult != null)
96191
{
97-
var timeSince = DateTime.Now - _lastTestTime;
98-
var timeStr = timeSince.TotalMinutes < 60
99-
? $"{(int)timeSince.TotalMinutes}m ago"
100-
: $"{(int)timeSince.TotalHours}h ago";
101-
102192
results.Add(new Result
103193
{
104194
Title = $"↓ {_lastResult.DownloadSpeed:F1} Mbps ↑ {_lastResult.UploadSpeed:F1} Mbps",
105-
SubTitle = $"Ping: {_lastResult.Ping:F0} ms • {_lastResult.ServerName}{timeStr}Enter to retest",
195+
SubTitle = $"Ping: {_lastResult.Ping:F0} ms • {_lastResult.ServerName} • Enter to retest",
106196
IcoPath = GetIcon(),
107197
Action = _ =>
108198
{
109199
_lastResult = null;
110200
_lastError = null;
111-
RunTest();
201+
_context?.API.ChangeQuery(_context.CurrentPluginMetadata.ActionKeyword + " start", true);
112202
return false;
113203
}
114204
});
@@ -196,18 +286,6 @@ private void RunTest()
196286
_currentDownloadSpeed = 0;
197287
_currentUploadSpeed = 0;
198288

199-
_updateTimer = new Timer(_ =>
200-
{
201-
if (_isTestRunning && _context != null)
202-
{
203-
try
204-
{
205-
_context.API.ChangeQuery(_context.CurrentPluginMetadata.ActionKeyword + " ", true);
206-
}
207-
catch { }
208-
}
209-
}, null, 300, 300);
210-
211289
var cliPath = await SpeedTestCLI.DownloadIfMissing(_context!);
212290

213291
var result = await SpeedTestCLI.Run(
@@ -227,6 +305,29 @@ private void RunTest()
227305
_lastResult = result;
228306
_lastTestTime = DateTime.Now;
229307
_lastError = null;
308+
309+
// record history with IPs from speedtest result
310+
var entry = new HistoryEntry
311+
{
312+
Time = DateTime.Now,
313+
DownloadSpeed = result?.DownloadSpeed ?? 0,
314+
UploadSpeed = result?.UploadSpeed ?? 0,
315+
Ping = result?.Ping ?? 0,
316+
ServerName = result?.ServerName ?? string.Empty,
317+
ResultUrl = result?.ResultUrl ?? string.Empty,
318+
InternalIP = result?.InternalIP ?? "unknown",
319+
ExternalIP = result?.ExternalIP ?? "unknown"
320+
};
321+
322+
_settings ??= new Settings();
323+
_settings.History.Add(entry);
324+
// keep only the last N entries
325+
var max = _settings.MaxHistoryEntries > 0 ? _settings.MaxHistoryEntries : 20;
326+
if (_settings.History.Count > max)
327+
_settings.History.RemoveRange(0, _settings.History.Count - max);
328+
329+
try { _context?.API.SaveSettingJsonStorage<Settings>(); } catch { }
330+
230331
}
231332
catch (Exception ex)
232333
{
@@ -237,25 +338,74 @@ private void RunTest()
237338
finally
238339
{
239340
_isTestRunning = false;
240-
_updateTimer?.Dispose();
241-
_updateTimer = null;
242-
243-
await Task.Delay(50);
341+
_refreshTimer?.Dispose();
342+
_refreshTimer = null;
343+
344+
// Trigger final refresh to show full detailed results
244345
if (_context != null)
245346
{
347+
await Task.Delay(100);
246348
try
247349
{
248-
_context.API.ChangeQuery(_context.CurrentPluginMetadata.ActionKeyword, false);
350+
_context.API.ChangeQuery(_context.CurrentPluginMetadata.ActionKeyword + " result", true);
249351
}
250352
catch { }
251353
}
252354
}
253355
});
254356
}
255357

358+
private async Task<string> GetExternalIpAsync()
359+
{
360+
try
361+
{
362+
using var http = new System.Net.Http.HttpClient { Timeout = TimeSpan.FromSeconds(5) };
363+
return (await http.GetStringAsync("https://api.ipify.org")).Trim();
364+
}
365+
catch { return "unknown"; }
366+
}
367+
368+
private Task<string> GetInternalIpAsync()
369+
{
370+
try
371+
{
372+
var host = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
373+
foreach (var ip in host.AddressList)
374+
{
375+
if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !System.Net.IPAddress.IsLoopback(ip))
376+
return Task.FromResult(ip.ToString());
377+
}
378+
return Task.FromResult("unknown");
379+
}
380+
catch
381+
{
382+
return Task.FromResult("unknown");
383+
}
384+
}
385+
256386
public string GetTranslatedPluginTitle() => "Speed Test";
257387
public string GetTranslatedPluginDescription() => "Test your internet connection speed";
388+
389+
public System.Windows.Controls.Control CreateSettingPanel()
390+
{
391+
return new SettingsControl(_context!);
392+
}
393+
}
394+
395+
public class Settings {
396+
public List<HistoryEntry> History { get; set; } = new List<HistoryEntry>();
397+
public int MaxHistoryEntries { get; set; } = 20;
258398
}
259399

260-
public class Settings { }
400+
public class HistoryEntry
401+
{
402+
public DateTime Time { get; set; }
403+
public double DownloadSpeed { get; set; }
404+
public double UploadSpeed { get; set; }
405+
public double Ping { get; set; }
406+
public string ServerName { get; set; } = "";
407+
public string ResultUrl { get; set; } = "";
408+
public string InternalIP { get; set; } = "";
409+
public string ExternalIP { get; set; } = "";
410+
}
261411
}

0 commit comments

Comments
 (0)