Skip to content

Commit

Permalink
Allow action filtering on redux dev tools (fixes #383) (#389)
Browse files Browse the repository at this point in the history
* Support action filtering in Redux Dev Tools

* Add unit tests
  • Loading branch information
mrpmorris committed Oct 19, 2023
1 parent f54be56 commit 4f63723
Show file tree
Hide file tree
Showing 21 changed files with 288 additions and 94 deletions.
6 changes: 3 additions & 3 deletions Docs/releases.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Releases

## New in 5.10
* Support Action Filtering in Redux Dev Tools ([#383](https://github.com/mrpmorris/Fluxor/issues/383))
## New in 5.9
* Adds additional useful information to exception thrown by `DisposableAction` ([#425](https://github.com/mrpmorris/Fluxor/issues/425))
* Fix deadlock scenario when dispatching actions from an effect triggered by store activation ([#426](https://github.com/mrpmorris/Fluxor/issues/426))

## New in 5.8
* Fixes potential for deadlock ([#407](https://github.com/mrpmorris/Fluxor/issues/407))

## New in 5.7
* Fixes potential for deadlock ([#407](https://github.com/mrpmorris/Fluxor/issues/407))## New in 5.7
* Fixes memory leak when using `ActionSubscriber` or `SubscribeToAction` ([#378](https://github.com/mrpmorris/Fluxor/issues/378))

## New in 5.6
Expand Down
19 changes: 18 additions & 1 deletion Source/Fluxor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tutorials", "Tutorials", "{1A6663AC-4F1F-40AD-8D03-0B0977ED466D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2279CD95-5886-4777-B019-E66683CE32F1}"
ProjectSection(SolutionItems) = preProject
Tests\Directory.Build.props = Tests\Directory.Build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C2B1D7B3-E943-4EA3-88B0-94433B49080C}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Lib\Directory.Build.props = Lib\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluxor", "Lib\Fluxor\Fluxor.csproj", "{863909D3-7E81-4240-8C0A-6F57768D28FF}"
Expand Down Expand Up @@ -119,6 +121,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluxorBlazorWeb.EffectsTuto
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluxorBlazorWeb.ActionSubscriberTutorial.Contracts", "Tutorials\02-Blazor\02E-ActionSubscriber\FluxorBlazorWeb.ActionSubscriberTutorial\Shared\FluxorBlazorWeb.ActionSubscriberTutorial.Contracts.csproj", "{AE3CD639-5DE8-43ED-9CE4-8498548ADD28}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluxor.Blazor.Web.ReduxDevTools.UnitTests", "Tests\Fluxor.Blazor.Web.ReduxDevTools.UnitTests\Fluxor.Blazor.Web.ReduxDevTools.UnitTests.csproj", "{FD3207E5-1DE9-4526-9C2A-4C18129992D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -393,6 +397,18 @@ Global
{AE3CD639-5DE8-43ED-9CE4-8498548ADD28}.Release|iPhone.Build.0 = Release|Any CPU
{AE3CD639-5DE8-43ED-9CE4-8498548ADD28}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{AE3CD639-5DE8-43ED-9CE4-8498548ADD28}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Debug|iPhone.Build.0 = Debug|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Release|Any CPU.Build.0 = Release|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Release|iPhone.ActiveCfg = Release|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Release|iPhone.Build.0 = Release|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{FD3207E5-1DE9-4526-9C2A-4C18129992D9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -431,6 +447,7 @@ Global
{3E8452CA-14A3-432E-8378-99EAC13E5EED} = {B5E9B3FC-1304-4A74-B0A9-607282AB2FD0}
{7A6B9736-6DE1-4B52-8A88-D433C8917731} = {730B26CD-CADC-4CF0-8A1D-3BB174692A40}
{AE3CD639-5DE8-43ED-9CE4-8498548ADD28} = {B5E9B3FC-1304-4A74-B0A9-607282AB2FD0}
{FD3207E5-1DE9-4526-9C2A-4C18129992D9} = {2279CD95-5886-4777-B019-E66683CE32F1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B1F2E0DF-C651-48A3-83E3-3B77D34EC3A2}
Expand Down
11 changes: 2 additions & 9 deletions Source/Lib/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,15 @@
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>

<AssemblyOriginatorKeyFile>..\..\..\..\..\MrPMorris.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<DelaySign>false</DelaySign>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly Condition="'$(Configuration)'=='Release'">true</SignAssembly>
<GeneratePackageOnBuild Condition="'$(Configuration)'=='Release'">true</GeneratePackageOnBuild>
</PropertyGroup>

<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>NU5118</NoWarn>
</PropertyGroup>

<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.*" PrivateAssets="All" />
</ItemGroup>

</Project>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<LangVersion>11</LangVersion>
<Product>ReduxDevTools for Fluxor Blazor (Web)</Product>
<Description>Middleware link from a Blazor (Web) Fluxor store to the Redux Dev Tools browser extension for Chrome.</Description>
<PackageIcon>fluxor-blazor-reduxdevtools-small.png</PackageIcon>
<PackageTags>Redux Flux DotNet CSharp Blazor RazorComponents ReduxDevTools</PackageTags>
<SignAssembly Condition="'$(Configuration)'=='Release'">True</SignAssembly>
</PropertyGroup>

<PropertyGroup>
<RazorLangVersion>3.0</RazorLangVersion>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />

<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Fluxor.Blazor.Web.ReduxDevTools.Internal.CallbackObjects
{
public class BaseCallbackObject<TPayload>
where TPayload : BasePayload
{
#pragma warning disable IDE1006 // Naming Styles
public string type { get; set; }
public TPayload payload { get; set; }
#pragma warning restore IDE1006 // Naming Styles
}

public class BaseCallbackObject : BaseCallbackObject<BasePayload> { }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects
namespace Fluxor.Blazor.Web.ReduxDevTools.Internal.CallbackObjects
{
internal class BasePayload
public class BasePayload
{
#pragma warning disable IDE1006 // Naming Styles
public string type { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Dynamic;

namespace Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects
namespace Fluxor.Blazor.Web.ReduxDevTools.Internal.CallbackObjects
{
internal class JumpToStateCallback: BaseCallbackObject<JumpToStatePayload>
public class JumpToStateCallback : BaseCallbackObject<JumpToStatePayload>
{
#pragma warning disable IDE1006 // Naming Styles
public string state { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects
namespace Fluxor.Blazor.Web.ReduxDevTools.Internal.CallbackObjects
{
internal class JumpToStatePayload : BasePayload
public class JumpToStatePayload : BasePayload
{
#pragma warning disable IDE1006 // Naming Styles
public int index { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
using Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects;
using Fluxor.Blazor.Web.ReduxDevTools.Internal.CallbackObjects;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Fluxor.Blazor.Web.ReduxDevTools
namespace Fluxor.Blazor.Web.ReduxDevTools.Internal
{
public interface IReduxDevToolsInterop
{
bool DevToolsBrowserPluginDetected { get; }
Func<JumpToStateCallback, Task> OnJumpToState { get; set; }
Func<Task> OnCommit { get; set; }
ValueTask InitializeAsync(IDictionary<string, object> state);
Task<object> DispatchAsync(
object action,
IDictionary<string, object> state,
string stackTrace);
Task DevToolsCallback(string messageAsJson);
}

/// <summary>
/// Interop for dev tools
/// </summary>
internal sealed class ReduxDevToolsInterop : IDisposable
internal sealed class ReduxDevToolsInterop : IDisposable, IReduxDevToolsInterop
{
public const string DevToolsCallbackId = "DevToolsCallback";
public bool DevToolsBrowserPluginDetected { get; private set; }
public Func<JumpToStateCallback, Task> OnJumpToState;
public Func<Task> OnCommit;
public Func<JumpToStateCallback, Task> OnJumpToState { get; set; }
public Func<Task> OnCommit { get; set; }

private const string FluxorDevToolsId = "__FluxorDevTools__";
private const string FromJsDevToolsDetectedActionTypeName = "detected";
Expand All @@ -39,7 +52,7 @@ internal sealed class ReduxDevToolsInterop : IDisposable
DotNetRef = DotNetObjectReference.Create(this);
}

internal async ValueTask InitializeAsync(IDictionary<string, object> state)
public async ValueTask InitializeAsync(IDictionary<string, object> state)
{
IsInitializing = true;
try
Expand All @@ -52,7 +65,7 @@ internal async ValueTask InitializeAsync(IDictionary<string, object> state)
}
}

internal async Task<object> DispatchAsync(
public async Task<object> DispatchAsync(
object action,
IDictionary<string, object> state,
string stackTrace)
Expand Down Expand Up @@ -113,9 +126,9 @@ void IDisposable.Dispose()
}

private static bool IsDotNetReferenceObject(object x) =>
(x is not null)
&& (x.GetType().IsGenericType)
&& (x.GetType().GetGenericTypeDefinition() == typeof(DotNetObjectReference<>));
x is not null
&& x.GetType().IsGenericType
&& x.GetType().GetGenericTypeDefinition() == typeof(DotNetObjectReference<>);

private ValueTask<TResult> InvokeFluxorDevToolsMethodAsync<TResult>(string identifier, params object[] args)
{
Expand Down Expand Up @@ -167,7 +180,7 @@ internal static string GetClientScripts(ReduxDevToolsMiddlewareOptions options)
// Notify Fluxor of the presence of the browser plugin
const detectedMessage = {{
payload: {{
type: '{ReduxDevToolsInterop.FromJsDevToolsDetectedActionTypeName}'
type: '{FromJsDevToolsDetectedActionTypeName}'
}}
}};
const detectedMessageAsJson = JSON.stringify(detectedMessage);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
using Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects;
using Fluxor.Extensions;
using System;
using Fluxor.Blazor.Web.ReduxDevTools.Internal;
using Fluxor.Blazor.Web.ReduxDevTools.Internal.CallbackObjects;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Fluxor.Blazor.Web.ReduxDevTools
{
/// <summary>
/// Middleware for interacting with the Redux Devtools extension for Chrome
/// </summary>
internal sealed class ReduxDevToolsMiddleware : WebMiddleware
public sealed class ReduxDevToolsMiddleware : WebMiddleware
{
private readonly object SyncRoot = new();
private Task TailTask = Task.CompletedTask;
private int SequenceNumberOfCurrentState = 0;
private int SequenceNumberOfLatestState = 0;
private readonly ReduxDevToolsMiddlewareOptions Options;
private IStore Store;
private readonly ReduxDevToolsInterop ReduxDevToolsInterop;
private readonly IReduxDevToolsInterop ToolsInterop;
private readonly IJsonSerialization JsonSerialization;

/// <summary>
/// Creates a new instance of the middleware
/// </summary>
public ReduxDevToolsMiddleware(
ReduxDevToolsInterop reduxDevToolsInterop,
IReduxDevToolsInterop reduxDevToolsInterop,
ReduxDevToolsMiddlewareOptions options,
IJsonSerialization jsonSerialization = null)
{
Options = options;
ReduxDevToolsInterop = reduxDevToolsInterop;
ReduxDevToolsInterop.OnJumpToState = OnJumpToState;
ReduxDevToolsInterop.OnCommit = OnCommit;
ToolsInterop = reduxDevToolsInterop;
ToolsInterop.OnJumpToState = OnJumpToState;
ToolsInterop.OnCommit = OnCommit;
JsonSerialization = jsonSerialization ?? new Serialization.NewtonsoftJsonAdapter();
}

Expand All @@ -45,7 +43,7 @@ internal sealed class ReduxDevToolsMiddleware : WebMiddleware
public async override Task InitializeAsync(IDispatcher dispatcher, IStore store)
{
Store = store;
await ReduxDevToolsInterop.InitializeAsync(GetState());
await ToolsInterop.InitializeAsync(GetState());
}

/// <see cref="IMiddleware.MayDispatchAction(object)"/>
Expand All @@ -55,6 +53,9 @@ public async override Task InitializeAsync(IDispatcher dispatcher, IStore store)
/// <see cref="IMiddleware.AfterDispatch(object)"/>
public override void AfterDispatch(object action)
{
if (Options.ActionFilters.Length > 0 && !Options.ActionFilters.All(filter => filter(action)))
return;

string stackTrace = null;
int maxItems = Options.StackTraceLimit == 0 ? int.MaxValue : Options.StackTraceLimit;
if (Options.StackTraceEnabled)
Expand All @@ -69,7 +70,7 @@ public override void AfterDispatch(object action)
{
IDictionary<string, object> state = GetState();
TailTask = TailTask
.ContinueWith(_ => ReduxDevToolsInterop.DispatchAsync(action, state, stackTrace)).Unwrap();
.ContinueWith(_ => ToolsInterop.DispatchAsync(action, state, stackTrace)).Unwrap();
}

// As actions can only be executed if not in a historical state (yes, "a" historical, pronounce your H!)
Expand All @@ -91,7 +92,7 @@ private async Task OnCommit()
// Wait for fire+forget state notifications to ReduxDevTools to dequeue
await TailTask.ConfigureAwait(false);

await ReduxDevToolsInterop.InitializeAsync(GetState());
await ToolsInterop.InitializeAsync(GetState());
SequenceNumberOfCurrentState = SequenceNumberOfLatestState;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Fluxor.Blazor.Web.ReduxDevTools;
using Fluxor.Blazor.Web.ReduxDevTools.Internal;
using Fluxor.DependencyInjection;
using Fluxor.Extensions;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace Fluxor
namespace Fluxor.Blazor.Web.ReduxDevTools
{
public static class OptionsReduxDevToolsExtensions
{
Expand Down

0 comments on commit 4f63723

Please sign in to comment.