Skip to content

Commit

Permalink
Release/4.1 (#193)
Browse files Browse the repository at this point in the history
* JSON serialization control #168 (#170)

* Fix ``` formatting
  • Loading branch information
mrpmorris committed May 11, 2021
1 parent 0fb6404 commit b616bcd
Show file tree
Hide file tree
Showing 25 changed files with 973 additions and 613 deletions.
6 changes: 3 additions & 3 deletions Docs/disposable-callback-not-disposed.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ or `FluxorLayout` and not called `base.Dispose(disposed)`.

**Fix:** If you override a method, make sure you call the base method.

```C#
```c#
protected override void Dispose(bool disposed)
{
base.Dispose(disposed);
Expand All @@ -32,10 +32,10 @@ disposing the result.
make sure you dispose it. Note that the following example is for
advanced use cases only (middleware).

```C#
```c#
using (Store.BeginMiddlewareChange())
{
//etc
//etc
}
```

Expand Down
73 changes: 39 additions & 34 deletions Docs/releases.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# Releases

### New in 4.0
## New in 4.1
* Allow custom control over JSON serialisation in Redux Dev Tools - see
[Redux Dev Tools](../Tutorials/02-Blazor/02D-ReduxDevToolsTutorial/README.md) docs.


## New in 4.0
* Changed `Effect<T>.HandleAsync` from `protected` to `public` to make unit testing easier
* Added `Options` to `.UseReduxDevTools()` middleware extension

### New in 3.9
## New in 3.9
* Support added for .NET 5.0

### New in 3.8
## New in 3.8
* Allow state notification throttling to reduce time spent rendering ([#114](https://github.com/mrpmorris/Fluxor/issues/114)) - see:
* `FluxorComponent.MaximumStateChangedNotificationsPerSecond`
* `FluxorLayout.MaximumStateChangedNotificationsPerSecond`
Expand Down Expand Up @@ -64,11 +69,11 @@ public class TestStringReducer: AbstractGenericStateReducers<string>
}

public abstract class AbstractGenericStateReducers<T>
where T : IEquatable<T>
where T : IEquatable<T>
{
[ReducerMethod]
public static TestState<T> ReduceRemoveItemAction(TestState<T> state, RemoveItemAction<T> action) =>
new TestState<T>(state.Items.Where(x => !x.Equals(action.Item)).ToArray());
[ReducerMethod]
public static TestState<T> ReduceRemoveItemAction(TestState<T> state, RemoveItemAction<T> action) =>
new TestState<T>(state.Items.Where(x => !x.Equals(action.Item)).ToArray());
}
```

Expand All @@ -77,36 +82,36 @@ public abstract class AbstractGenericStateReducers<T>
```c#
public class GenericEffectClassForMyAction : AbstractGenericEffectClass<MyAction>
{
public GenericEffectClassForMyAction(SomeService someService) : base(someService)
{
}
public GenericEffectClassForMyAction(SomeService someService) : base(someService)
{
}
}

public class GenericEffectClassForAnotherAction : AbstractGenericEffectClass<AnotherAction>
{
public GenericEffectClassForAnotherAction(SomeService someService) : base(someService)
{
}
public GenericEffectClassForAnotherAction(SomeService someService) : base(someService)
{
}
}

public abstract class AbstractGenericEffectClass<T>
{
private readonly ISomeService SomeService;

protected AbstractGenericEffectClass(ISomeService someService)
{
SomeService = someService;
}

[EffectMethod]
public Task HandleTheActionAsync(T action, IDispatcher dispatcher)
{
return SomeService.DoSomethingAsync(action);
}
private readonly ISomeService SomeService;

protected AbstractGenericEffectClass(ISomeService someService)
{
SomeService = someService;
}

[EffectMethod]
public Task HandleTheActionAsync(T action, IDispatcher dispatcher)
{
return SomeService.DoSomethingAsync(action);
}
}
```

### New in 3.7
## New in 3.7
* Fix for ([#84](https://github.com/mrpmorris/Fluxor/issues/84) -
Allow observer to unsubscribe from all subscriptions whilst executing
the callback from a previous subscription
Expand All @@ -116,42 +121,42 @@ public abstract class AbstractGenericEffectClass<T>
* Fix for ([#77](https://github.com/mrpmorris/Fluxor/issues/87)) -
Exception thrown initialising store in .NET 5.

### New in 3.6
## New in 3.6
* Ensure synchronous effects are executed synchronously ([#76](https://github.com/mrpmorris/fluxor/issues/76)) -
Reverts changes for [(#74) Endless loop redirects](https://github.com/mrpmorris/Fluxor/issues/74) as
these are no longer occur.

### New in 3.5
## New in 3.5
* Bug fix for ([#74](https://github.com/mrpmorris/Fluxor/issues/74)) - Handle endless loop redirects caused by Routing middleware.

### New in 3.4
## New in 3.4
* **Breaking change**: `FluxorException` is now an `abstract` class.
* Unhandled exceptions in Effects can now notify the UI.

### New in 3.3
## New in 3.3
* **Breaking change**: `EffectMethod` and `ReducerMethod` decorated methods must now be public - although they can be methods of internal classes.
* New `IActionSubscriber` to receive notifications before actions are reduced into state and before Effects are triggered
* More speed improvements in `options.ScanAssemblies`
* Subscriptions to the `StateChanged` event will now be triggered before FluxorComponent/FluxorLayout notified of changes (so manual subscriptions are executed before rendering)
* Added link with more information for when `DisposableCallback` throws an error because it has not been disposed

### New in 3.2
## New in 3.2
* Improved speed of app start-up when using `options.ScanAssemblies`
* Assemblies are now signed
* Set project options to treat all warnings as errors

### New in 3.1.1
## New in 3.1.1
* Fixed bug that caused exception when using `.ConfigureAwait` in an Effect ([#20](https://github.com/mrpmorris/Fluxor/issues/20))
* Ensured add/remove on events are thread safe ([#23](https://github.com/mrpmorris/Fluxor/issues/23))
* Made it easier to find the source of DisposableCallback instances that are not disposed ([#24](https://github.com/mrpmorris/Fluxor/issues/24))
* State properties were not discovered if they were declared as private in a base class ([#25](https://github.com/mrpmorris/Fluxor/issues/25))
* Handle disposing of partially created DisposableAction ([#29](https://github.com/mrpmorris/Fluxor/issues/29))

### New in 3.1.0
## New in 3.1.0
* Used Newtonsoft entirely for JS interop to ReduxDevTools to prevent serialization errors ([#7](https://github.com/mrpmorris/Fluxor/issues/7))
* Added new FluxorLayout for auto-subscribing to state ([#8](https://github.com/mrpmorris/Fluxor/issues/8))

### New in 3.0.2
## New in 3.0.2
* Bug fix for ([#134](https://github.com/mrpmorris/blazor-fluxor/issues/134)) - URLs not taking into account query parameters
* Update NuGet package icons.

Expand Down
6 changes: 3 additions & 3 deletions Source/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>

<Version>4.0.0</Version>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion>
<Version>4.1.0</Version>
<AssemblyVersion>4.1.0.0</AssemblyVersion>
<FileVersion>4.1.0.0</FileVersion>

<Authors>Peter Morris</Authors>
<Company />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>

Expand Down
16 changes: 16 additions & 0 deletions Source/Fluxor.Blazor.Web.ReduxDevTools/IJsonSerialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Fluxor.Blazor.Web.ReduxDevTools
{
public interface IJsonSerialization
{
object Deserialize(string json, Type type);
string Serialize(object source, Type type);
}

public static class IJsonSerializationExtensions
{
public static T Deserialize<T>(this IJsonSerialization instance, string json) =>
(T)instance.Deserialize(json, typeof(T));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ namespace Fluxor
{
public static class OptionsReduxDevToolsExtensions
{
public static Options UseReduxDevTools(
this Options options,
/// <summary>
/// Enables interaction with the brower tool Redux Dev Tools
/// </summary>
/// <param name="options">The current options</param>
/// <param name="updateReduxOptions">An action to update the options</param>
/// <returns></returns>
public static FluxorOptions UseReduxDevTools(
this FluxorOptions options,
Action<ReduxDevToolsMiddlewareOptions> updateReduxOptions = null)
{
var reduxOptions = new ReduxDevToolsMiddlewareOptions();
var reduxOptions = new ReduxDevToolsMiddlewareOptions(options);
updateReduxOptions?.Invoke(reduxOptions);

options.AddMiddleware<ReduxDevToolsMiddleware>();
Expand Down
19 changes: 11 additions & 8 deletions Source/Fluxor.Blazor.Web.ReduxDevTools/ReduxDevToolsInterop.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects;
using Microsoft.JSInterop;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -23,19 +22,21 @@ internal sealed class ReduxDevToolsInterop : IDisposable
private const string ToJsDispatchMethodName = "dispatch";
private const string ToJsInitMethodName = "init";
private bool IsInitializing;
private ReduxDevToolsMiddlewareOptions Options;
private readonly IJSRuntime JSRuntime;
private DotNetObjectReference<ReduxDevToolsInterop> DotNetRef;
private readonly IJsonSerialization JsonSerialization;
private readonly DotNetObjectReference<ReduxDevToolsInterop> DotNetRef;

/// <summary>
/// Creates an instance of the dev tools interop
/// </summary>
/// <param name="jsRuntime"></param>
public ReduxDevToolsInterop(IJSRuntime jsRuntime, ReduxDevToolsMiddlewareOptions options)
public ReduxDevToolsInterop(
IJSRuntime jsRuntime,
IJsonSerialization jsonSerialization = null)
{
JSRuntime = jsRuntime;
JsonSerialization = jsonSerialization ?? new Serialization.NewtonsoftJsonAdapter();
DotNetRef = DotNetObjectReference.Create(this);
Options = options;
}

internal async ValueTask InitializeAsync(IDictionary<string, object> state)
Expand Down Expand Up @@ -65,7 +66,7 @@ public async Task DevToolsCallback(string messageAsJson)
if (string.IsNullOrWhiteSpace(messageAsJson))
return;

var message = JsonConvert.DeserializeObject<BaseCallbackObject>(messageAsJson);
var message = JsonSerialization.Deserialize<BaseCallbackObject>(messageAsJson);
switch (message?.payload?.type)
{
case FromJsDevToolsDetectedActionTypeName:
Expand All @@ -87,7 +88,7 @@ public async Task DevToolsCallback(string messageAsJson)
Func<JumpToStateCallback, Task> jumpToState = OnJumpToState;
if (jumpToState != null)
{
var callbackInfo = JsonConvert.DeserializeObject<JumpToStateCallback>(messageAsJson);
var callbackInfo = JsonSerialization.Deserialize<JumpToStateCallback>(messageAsJson);
Task task = jumpToState(callbackInfo);
if (task != null)
await task;
Expand Down Expand Up @@ -119,7 +120,9 @@ private ValueTask<TResult> InvokeFluxorDevToolsMethodAsync<TResult>(string ident
for (int i = 0; i < args.Length; i++)
{
if (!IsDotNetReferenceObject(args[i]))
args[i] = Newtonsoft.Json.JsonConvert.SerializeObject(args[i]);
args[i] = JsonSerialization.Serialize(
source: args[i],
type: args[i]?.GetType() ?? typeof(object));
}
}

Expand Down
17 changes: 10 additions & 7 deletions Source/Fluxor.Blazor.Web.ReduxDevTools/ReduxDevToolsMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Fluxor.Blazor.Web.ReduxDevTools.CallbackObjects;
using Fluxor.Extensions;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
Expand All @@ -17,21 +16,24 @@ internal sealed class ReduxDevToolsMiddleware : WebMiddleware
private SpinLock SpinLock = new SpinLock();
private int SequenceNumberOfCurrentState = 0;
private int SequenceNumberOfLatestState = 0;
private ReduxDevToolsMiddlewareOptions Options;
private readonly ReduxDevToolsMiddlewareOptions Options;
private IStore Store;
private readonly ReduxDevToolsInterop ReduxDevToolsInterop;
private readonly IJsonSerialization JsonSerialization;

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

/// <see cref="IMiddleware.GetClientScripts"/>
Expand Down Expand Up @@ -89,16 +91,17 @@ private async Task OnJumpToState(JumpToStateCallback callbackInfo)
SequenceNumberOfCurrentState = callbackInfo.payload.actionId;
using (Store.BeginInternalMiddlewareChange())
{
var newFeatureStates = JsonConvert.DeserializeObject<Dictionary<string, object>>(callbackInfo.state);
var newFeatureStates = JsonSerialization.Deserialize<Dictionary<string, object>>(callbackInfo.state);
foreach (KeyValuePair<string, object> newFeatureState in newFeatureStates)
{
// Get the feature with the given name
if (!Store.Features.TryGetValue(newFeatureState.Key, out IFeature feature))
continue;

object stronglyTypedFeatureState = JsonConvert.DeserializeObject(
value: newFeatureState.Value.ToString(),
type: feature.GetStateType());
object stronglyTypedFeatureState = JsonSerialization
.Deserialize(
json: newFeatureState.Value.ToString(),
type: feature.GetStateType());

// Now set the feature's state to the deserialized object
feature.RestoreState(stronglyTypedFeatureState);
Expand Down
Loading

0 comments on commit b616bcd

Please sign in to comment.