Add RecordKey for precise history matching and refactor

Added a `RecordKey` property to `HistoryItem` for unique identification of history records, enabling more accurate matching during queries and executions. Updated `HistoryHelper` methods to utilize `RecordKey` for matching, with fallback to `Title` and `SubTitle`. Enhanced `GetExecuteAction` with error handling, nullable reference types, and improved matching logic. Included `RecordKey` in `History` object creation. Enabled nullable reference types in `HistoryHelper.cs` for better code safety. Refactored code for clarity and maintainability.
This commit is contained in:
Jack251970 2025-10-13 15:27:06 +08:00
parent cc397d45a9
commit e3527f47ea
3 changed files with 35 additions and 11 deletions

View file

@ -81,6 +81,7 @@ namespace Flow.Launcher.Storage
SubTitle = result.SubTitle,
PluginID = result.PluginID,
RawQuery = result.OriginQuery.RawQuery,
RecordKey = result.RecordKey,
ExecutedDateTime = DateTime.Now,
ExecuteAction = result.Action
};

View file

@ -6,40 +6,62 @@ using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Storage;
#nullable enable
public static class HistoryHelper
{
internal static List<HistoryItem> PopulateActions(this List<HistoryItem> items, bool isQuery)
internal static List<HistoryItem> PopulateActions(this List<HistoryItem> items, bool isQuery)
{
foreach (var item in items)
{
if (item.QueryAction != null && item.ExecuteAction != null) continue;
if (isQuery && item.QueryAction == null) item.QueryAction = GetQueryAction(item.RawQuery);
if (!isQuery && item.ExecuteAction == null) item.ExecuteAction = GetExecuteAction(item.PluginID, item.RawQuery, item.Title, item.SubTitle);
if (!isQuery && item.ExecuteAction == null) item.ExecuteAction = GetExecuteAction(item.PluginID, item.RawQuery, item.Title, item.SubTitle, item.RecordKey) ?? GetQueryAction(item.RawQuery);
}
return items;
}
public static Func<ActionContext, bool> GetQueryAction(string query)
public static Func<ActionContext, bool> GetQueryAction(string rawQuery)
{
return _ =>
{
App.API.BackToQueryResults();
App.API.ChangeQuery(query);
App.API.ChangeQuery(rawQuery);
return false;
};
}
private static Func<ActionContext, bool> GetExecuteAction(string pluginId, string rawQuery, string title, string subTitle)
private static Func<ActionContext, bool>? GetExecuteAction(string pluginId, string rawQuery, string title, string subTitle, string recordKey)
{
var plugin = PluginManager.GetPluginForId(pluginId);
if (plugin == null) return null;
var query = QueryBuilder.Build(rawQuery, PluginManager.NonGlobalPlugins);
var freshResults = plugin.Plugin
.QueryAsync(query, CancellationToken.None)
.GetAwaiter()
.GetResult();
return freshResults?.FirstOrDefault(r => r.Title == title
&& r.SubTitle == subTitle)?.Action;
if (query == null) return null;
try
{
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
var freshResults = plugin.Plugin
.QueryAsync(query, CancellationToken.None)
.GetAwaiter()
.GetResult();
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
// Try to match by record key first if it is valid, otherwise fall back to title + subtitle match
if (string.IsNullOrEmpty(recordKey))
{
return freshResults?.FirstOrDefault(r => r.Title == title && r.SubTitle == subTitle)?.Action;
}
else
{
return freshResults?.FirstOrDefault(r => r.RecordKey == recordKey)?.Action ??
freshResults?.FirstOrDefault(r => r.Title == title && r.SubTitle == subTitle)?.Action;
}
}
catch
{
return null;
}
}
}

View file

@ -10,6 +10,7 @@ public class HistoryItem
public string SubTitle { get; set; } = string.Empty;
public string PluginID { get; set; } = string.Empty;
public string RawQuery { get; set; }
public string RecordKey { get; set; } = string.Empty;
public DateTime ExecutedDateTime { get; set; }
[JsonIgnore]