Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
71 changes: 70 additions & 1 deletion Flow.Launcher.Test/Plugins/CalculatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Reflection;
using Flow.Launcher.Plugin.Calculator;
using Flow.Launcher.Plugin.Calculator.Storage;
using Mages.Core;
using NUnit.Framework;
using NUnit.Framework.Legacy;
Expand All @@ -17,7 +18,8 @@ public class CalculatorPluginTest
DecimalSeparator = DecimalSeparator.UseSystemLocale,
MaxDecimalPlaces = 10,
ShowErrorMessage = false, // Make sure we return the empty results when error occurs
UseThousandsSeparator = true // Default value
UseThousandsSeparator = true, // Default value
EnableHistory = true,
};
private readonly Engine _engine = new(new Configuration
{
Expand All @@ -40,6 +42,14 @@ public CalculatorPluginTest()
if (engineField == null)
Assert.Fail("Could not find static field 'MagesEngine' on Flow.Launcher.Plugin.Calculator.Main");
engineField.SetValue(null, _engine);

SetHistory(new History());
}

[SetUp]
public void SetUp()
{
SetHistory(new History());
}

[Test]
Expand Down Expand Up @@ -80,6 +90,34 @@ public void ThousandsSeparatorTest_LargeNumber()
ClassicAssert.AreEqual("1234567", result);
}

[Test]
public void CalculationHistory_IsStoredWhenEnabled()
{
_settings.EnableHistory = true;

_plugin.Query(new Plugin.Query { Search = "1+1" });
WaitForHistoryDebounce();

ClassicAssert.AreEqual(1, GetHistory().Items.Count);
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
ClassicAssert.AreEqual("1+1", GetHistory().Items[0].Query);
ClassicAssert.AreEqual("2", GetHistory().Items[0].CopyText);
ClassicAssert.IsNotEmpty(GetHistory().Items[0].SubTitle);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[Test]
public void CalculationHistory_IsNotReturnedWhenDisabled()
{
_settings.EnableHistory = true;
_plugin.Query(new Plugin.Query { Search = "1+1" });
WaitForHistoryDebounce();

_settings.EnableHistory = false;
var results = _plugin.Query(new Plugin.Query { Search = "2+2" });

ClassicAssert.AreEqual(1, results.Count);
ClassicAssert.AreEqual("4", results[0].Title);
}
Comment thread
Jack251970 marked this conversation as resolved.

// Basic operations
[TestCase(@"1+1", "2")]
[TestCase(@"2-1", "1")]
Expand Down Expand Up @@ -130,5 +168,36 @@ private string GetCalculationResult(string expression)
});
return results.Count > 0 ? results[0].Title : string.Empty;
}

private void WaitForHistoryDebounce()
{
var flushPendingItemMethod = typeof(History).GetMethod("FlushPendingItem", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(flushPendingItemMethod, Is.Not.Null,
"Could not find method 'FlushPendingItem' on Flow.Launcher.Plugin.Calculator.Storage.History");

flushPendingItemMethod!.Invoke(GetHistory(), null);
}

private History GetHistory()
{
var historyProperty = typeof(Main).GetProperty("History", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(historyProperty, Is.Not.Null,
"Could not find property 'History' on Flow.Launcher.Plugin.Calculator.Main");

var history = historyProperty!.GetValue(_plugin) as History;
Assert.That(history, Is.Not.Null,
"Property 'History' on Flow.Launcher.Plugin.Calculator.Main' was not a calculator History instance");

return history!;
}

private void SetHistory(History history)
{
var historyProperty = typeof(Main).GetProperty("History", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(historyProperty, Is.Not.Null,
"Could not find property 'History' on Flow.Launcher.Plugin.Calculator.Main");

historyProperty!.SetValue(_plugin, history);
}
}
}
23 changes: 23 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Flow.Launcher.Localization.Attributes;

namespace Flow.Launcher.Plugin.Calculator
{
/// <summary>
/// Represents the different modes for saving calculator calculations into the history.
/// </summary>
[EnumLocalize]
public enum HistoryCreationMode
{
/// <summary>
/// Saves calculations into the history automatically as the query is typed.
/// </summary>
[EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_calculator_history_creation_mode_on_query))]
OnQuery,

/// <summary>
/// Saves calculations into the history only when the action is executed (e.g. Enter is pressed).
/// </summary>
[EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_calculator_history_creation_mode_on_enter))]
OnEnter
}
}
101 changes: 101 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System;
using System.Globalization;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.Calculator.Storage;

namespace Flow.Launcher.Plugin.Calculator
{
/// <summary>
/// Helper class providing utility methods for formatting relative time strings and creating pending history items.
/// </summary>
internal static class HistoryHelper
{
/// <summary>
/// Formats a DateTime value into a localized relative time string (e.g. "just now", "5 minutes ago").
/// </summary>
/// <param name="context">The plugin init context used to retrieve localized strings.</param>
/// <param name="calculatedAt">The time when the calculation was recorded.</param>
/// <returns>A localized relative time delta string.</returns>
public static string GetTimeDeltaString(PluginInitContext context, DateTime calculatedAt)
{
var now = DateTime.Now;
var timeSpan = now - calculatedAt;

if (timeSpan.TotalSeconds < 0)
{
timeSpan = TimeSpan.Zero;
}

if (timeSpan.TotalSeconds < 60)
{
return context == null ? "just now" : Localize.flowlauncher_plugin_calculator_time_just_now();
}
if (timeSpan.TotalMinutes < 60)
{
var minutes = (int)timeSpan.TotalMinutes;
if (minutes == 1)
{
return context == null ? "1 minute ago" : Localize.flowlauncher_plugin_calculator_time_minute_ago();
}
return context == null ? $"{minutes} minutes ago" : Localize.flowlauncher_plugin_calculator_time_minutes_ago(minutes);
}
if (timeSpan.TotalHours < 24)
{
var hours = (int)timeSpan.TotalHours;
if (hours == 1)
{
return context == null ? "1 hour ago" : Localize.flowlauncher_plugin_calculator_time_hour_ago();
}
return context == null ? $"{hours} hours ago" : Localize.flowlauncher_plugin_calculator_time_hours_ago(hours);
}
if (timeSpan.TotalDays < 30)
{
var days = (int)timeSpan.TotalDays;
if (days == 1)
{
return context == null ? "1 day ago" : Localize.flowlauncher_plugin_calculator_time_day_ago();
}
return context == null ? $"{days} days ago" : Localize.flowlauncher_plugin_calculator_time_days_ago(days);
}
if (timeSpan.TotalDays < 365)
{
var months = (int)(timeSpan.TotalDays / 30);
if (months == 1)
{
return context == null ? "1 month ago" : Localize.flowlauncher_plugin_calculator_time_month_ago();
}
return context == null ? $"{months} months ago" : Localize.flowlauncher_plugin_calculator_time_months_ago(months);
}
var years = (int)(timeSpan.TotalDays / 365);
if (years == 1)
{
return context == null ? "1 year ago" : Localize.flowlauncher_plugin_calculator_time_year_ago();
}
return context == null ? $"{years} years ago" : Localize.flowlauncher_plugin_calculator_time_years_ago(years);
}

/// <summary>
/// Creates a <see cref="PendingHistoryItem"/> representing a calculation that is currently being typed by the user.
/// </summary>
/// <param name="context">The plugin init context.</param>
/// <param name="result">The query result object.</param>
/// <param name="calcResult">The text representation of the calculation result.</param>
/// <param name="expression">The math expression string.</param>
/// <returns>A new <see cref="PendingHistoryItem"/> instance.</returns>
public static PendingHistoryItem CreatePendingHistoryItem(PluginInitContext context, Result result, string calcResult, string expression)
{
var calculatedAt = DateTime.Now;
var copyToClipboard = context == null
? "Copy this number to the clipboard"
: Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard();
var timeDeltaStr = GetTimeDeltaString(context, calculatedAt);
var historySubtitle = context == null
? string.Format(CultureInfo.CurrentCulture, "Calculated {0}", timeDeltaStr)
: Localize.flowlauncher_plugin_calculator_history_subtitle(timeDeltaStr);
var subtitle =
$"{calcResult} - {copyToClipboard}" +
$"\n{historySubtitle}";
return new PendingHistoryItem(result, expression, result.Action, subtitle, calculatedAt);
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm this is from icons8 which we already give attribution to so is safe to use
https://icons8.com/icon/QDgOnr6UAOmg/time-machine

My concern is that at a small scale its not too readable

I found another alternative, which might be slightly more clear - though still not ideal
https://icons8.com/icons/set/history--os-windows--multi--technique-filled

Image

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 19 additions & 1 deletion Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,22 @@
<system:String x:Key="flowlauncher_plugin_calculator_use_thousands_separator">Show thousands separator in results</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_failed_to_copy">Copy failed, please try later</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_show_error_message">Show error message when calculation fails</system:String>
</ResourceDictionary>
<system:String x:Key="flowlauncher_plugin_calculator_enable_history">Enable History</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_subtitle">{0}</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_just_now">just now</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_minute_ago">1 minute ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_minutes_ago">{0} minutes ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_hour_ago">1 hour ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_hours_ago">{0} hours ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_day_ago">1 day ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_days_ago">{0} days ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_month_ago">1 month ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_months_ago">{0} months ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_year_ago">1 year ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_time_years_ago">{0} years ago</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode">History save mode</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode_on_query">Save on query</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode_on_enter">Save on Enter</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_history_creation_mode_warning">Warning: Due to the plugin's architecture, saving on query may register incomplete calculations in the history while you type.</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_copy_result_to_clipboard">Copy the result of this expression to the clipboard</system:String>
</ResourceDictionary>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, could you please revert changes in this file?

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0"?>
Comment thread
01Dri marked this conversation as resolved.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">

<system:String x:Key="flowlauncher_plugin_calculator_plugin_name">Calculadora</system:String>
Expand Down
Loading
Loading