-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a fuzzy search over the master exercise list
- Loading branch information
1 parent
fa36e09
commit 2b3cc44
Showing
21 changed files
with
16,414 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using LiftLog.Lib; | ||
using LiftLog.Lib.Serialization; | ||
using LiftLog.Ui.Services; | ||
|
||
namespace LiftLog.App.Services; | ||
|
||
public class AppBuiltInExerciseLoader : IBuiltInExerciseLoader | ||
{ | ||
public async Task<IReadOnlyList<DescribedExercise>> LoadBuiltInExercisesAsync() | ||
{ | ||
using var stream = await FileSystem.OpenAppPackageFileAsync("exercises.json"); | ||
if (stream == null) | ||
return []; | ||
|
||
var json = await JsonSerializer.DeserializeAsync( | ||
stream, | ||
JsonContext.Context.ListDescribedExercise | ||
); | ||
return json ?? []; | ||
} | ||
} | ||
|
||
[JsonSerializable(typeof(List<DescribedExercise>))] | ||
internal partial class JsonContext : JsonSerializerContext | ||
{ | ||
public static readonly JsonContext Context = new(JsonSerializerSettings.LiftLog); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
namespace LiftLog.Ui.Services; | ||
|
||
public record DescribedExercise( | ||
string Name, | ||
string? Force, | ||
string? Level, | ||
string? Mechanic, | ||
string? Equipment, | ||
string[]? PrimaryMuscles, | ||
string[]? SecondaryMuscles, | ||
string[]? Instructions, | ||
string? Category | ||
) | ||
{ | ||
public static DescribedExercise FromName(string name) => | ||
new(name, null, null, null, null, null, null, null, null); | ||
} | ||
|
||
public interface IBuiltInExerciseLoader | ||
{ | ||
Task<IReadOnlyList<DescribedExercise>> LoadBuiltInExercisesAsync(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
@inject IState<ExercisesState> State | ||
@inject IDispatcher Dispatcher | ||
|
||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent | ||
|
||
<Menu | ||
@ref="exerciseListMenu" | ||
default-focus="none" | ||
role="listbox" | ||
anchor="@Anchor" | ||
skip-restore-focus=true | ||
stay-open-on-outside-click=true | ||
stay-open-on-focusout=true | ||
anchor-corner="end-start" | ||
menu-corner="start-start" | ||
class="text-left"> | ||
@foreach (var describedExercise in filteredExercises) | ||
{ | ||
<MenuItem Label="@describedExercise.Name" Icon="" OnClick="() => ValueChanged.InvokeAsync(describedExercise.Name)"/> | ||
} | ||
</Menu> | ||
|
||
@code { | ||
private Menu? exerciseListMenu; | ||
[Parameter] public string Anchor { get; set; } = ""; | ||
|
||
[Parameter] public string Value { get; set; } = ""; | ||
|
||
[Parameter] public EventCallback<string> ValueChanged { get; set; } | ||
|
||
private IReadOnlyList<DescribedExercise> filteredExercises { get; set; } = new List<DescribedExercise>(); | ||
|
||
protected override void OnParametersSet() | ||
{ | ||
var searchTerm = Value.Replace(" ", ""); | ||
var pattern = String.Join(".*", searchTerm.AsEnumerable()); | ||
filteredExercises = State.Value.DescribedExercises | ||
.Where(e => IsMatch(e.Name, pattern)) | ||
.OrderByDescending(e => MostConsecutiveMatches(e.Name, searchTerm)) | ||
.ThenBy(e=>DistanceOfFirstCharacterFromStartOfPattern(e.Name, searchTerm)) | ||
.Take(5) | ||
.ToList(); | ||
|
||
base.OnParametersSet(); | ||
} | ||
|
||
protected override void OnInitialized(){ | ||
|
||
base.OnInitialized(); | ||
Dispatcher.Dispatch(new FetchDescribedExercisesAction()); | ||
} | ||
|
||
|
||
public void Open(){ | ||
exerciseListMenu?.Open(); | ||
} | ||
|
||
public void Close(){ | ||
exerciseListMenu?.Close(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System.Text.RegularExpressions; | ||
|
||
namespace LiftLog.Ui.Shared.Smart; | ||
|
||
public partial class ExerciseSearcher | ||
{ | ||
private static bool IsMatch(string? exerciseName, string pattern) | ||
{ | ||
if (string.IsNullOrWhiteSpace(exerciseName)) | ||
return false; | ||
return Regex.IsMatch(exerciseName, pattern, RegexOptions.IgnoreCase); | ||
} | ||
|
||
private static int DistanceOfFirstCharacterFromStartOfPattern( | ||
string exerciseName, | ||
string pattern | ||
) | ||
{ | ||
if (pattern.Length == 0) | ||
return int.MaxValue; | ||
var firstLetter = pattern[0]; | ||
var index = exerciseName.IndexOf(firstLetter, StringComparison.CurrentCultureIgnoreCase); | ||
return index == -1 ? int.MaxValue : index; | ||
} | ||
|
||
private static int MostConsecutiveMatches(string exerciseName, string pattern) | ||
{ | ||
if (pattern.Length == 0) | ||
return 0; | ||
var patternIndex = 0; | ||
var matches = 0; | ||
for (var i = 0; i < exerciseName.Length; i++) | ||
{ | ||
if (char.ToLower(exerciseName[i]) == char.ToLower(pattern[patternIndex])) | ||
{ | ||
patternIndex++; | ||
if (patternIndex == pattern.Length) | ||
return pattern.Length; | ||
} | ||
else | ||
{ | ||
patternIndex = 0; | ||
} | ||
} | ||
return matches; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using LiftLog.Lib; | ||
using LiftLog.Lib.Models; | ||
using LiftLog.Ui.Services; | ||
|
||
namespace LiftLog.Ui.Store.Exercises; | ||
|
||
public record SetDescribedExercisesAction(ImmutableListValue<DescribedExercise> DescribedExercises); | ||
|
||
public record FetchDescribedExercisesAction(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System.Collections.Immutable; | ||
using Fluxor; | ||
using LiftLog.Ui.Services; | ||
|
||
namespace LiftLog.Ui.Store.Exercises; | ||
|
||
public class ExercisesEffects( | ||
IBuiltInExerciseLoader builtInExerciseLoader, | ||
ProgressRepository progressRepository | ||
) | ||
{ | ||
[EffectMethod] | ||
public async Task HandleFetchDescribedExercisesAction( | ||
FetchDescribedExercisesAction _, | ||
IDispatcher dispatcher | ||
) | ||
{ | ||
var (builtInExercises, usedExercises) = await ( | ||
builtInExerciseLoader.LoadBuiltInExercisesAsync(), | ||
Task.Run( | ||
async () => | ||
await progressRepository | ||
.GetOrderedSessions() | ||
.SelectMany(x => | ||
x.RecordedExercises.Select(ex => ex.Blueprint.Name).ToAsyncEnumerable() | ||
) | ||
.Distinct() | ||
.Select(DescribedExercise.FromName) | ||
.ToListAsync() | ||
) | ||
); | ||
|
||
var describedExercises = builtInExercises | ||
.Concat(usedExercises) | ||
.DistinctBy(x => x.Name) | ||
.ToImmutableList(); | ||
|
||
dispatcher.Dispatch(new SetDescribedExercisesAction(describedExercises)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using Fluxor; | ||
|
||
namespace LiftLog.Ui.Store.Exercises; | ||
|
||
public class ExercisesFeature : Feature<ExercisesState> | ||
{ | ||
public override string GetName() => nameof(ExercisesFeature); | ||
|
||
protected override ExercisesState GetInitialState() => new([]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Fluxor; | ||
using LiftLog.Lib.Models; | ||
using LiftLog.Ui.Store.Exercises; | ||
|
||
namespace LiftLog.Ui.Store.Exercises; | ||
|
||
public static class ExercisesReducers | ||
{ | ||
[ReducerMethod] | ||
public static ExercisesState ReduceSetDescribedExercisesAction( | ||
ExercisesState state, | ||
SetDescribedExercisesAction action | ||
) => state with { DescribedExercises = action.DescribedExercises }; | ||
} |
Oops, something went wrong.