Skip to content

Commit

Permalink
Improved readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Pjotrtje committed Dec 28, 2022
1 parent 1aa9ed4 commit 8da5fff
Show file tree
Hide file tree
Showing 20 changed files with 66 additions and 33 deletions.
55 changes: 43 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Fluxor.Undo is a library to add redo/undo functionality to [Fluxor](https://gith

![Azure DevOps builds (branch)](https://img.shields.io/azure-devops/build/Pjotrtje/PvS/21/main)

![Demo](https://raw.githubusercontent.com/Pjotrtje/Fluxor.Undo/main/docs/undo_demo.gif)


## Goal
The aim of Fluxor.Undo is removing the hassle of implementing your own undo/redo functionality. The idea is inspired by [redux-undo](https://github.com/omnidan/redux-undo) although the implementation is completely different.

Expand All @@ -26,7 +29,7 @@ Change your state with FeatureStateAtrribute

```csharp
[FeatureState(Name = "Counter", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record CounterState(int ClickCount)
public sealed record CounterState(int ClickCount)
{
public static CounterState CreateInitialState()
=> new(0);
Expand All @@ -36,20 +39,20 @@ public record CounterState(int ClickCount)
to

```csharp
public record CounterState(int ClickCount);
public sealed record CounterState(int ClickCount);

[FeatureState(Name = "Counter", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
public sealed record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
{
public static UndoableCounterState CreateInitialState()
=> new() { Present = new(0) };
}

// Or when net6:
public record CounterState(int ClickCount);
public sealed record CounterState(int ClickCount);

[FeatureState(Name = "Counter", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record UndoableCounterState(CounterState Present) : Undoable<UndoableCounterState, CounterState>(Present)
public sealed record UndoableCounterState(CounterState Present) : Undoable<UndoableCounterState, CounterState>(Present)
{
public static UndoableCounterState CreateInitialState()
=> new(new CounterState(0));
Expand All @@ -59,7 +62,7 @@ public record UndoableCounterState(CounterState Present) : Undoable<UndoableCoun
or state with generic Feature

```csharp
public record CounterState(int ClickCount);
public sealed record CounterState(int ClickCount);

public sealed class CounterFeature : Feature<CounterState>
{
Expand All @@ -73,10 +76,9 @@ public sealed class CounterFeature : Feature<CounterState>

to


```csharp
public record CounterState(int ClickCount);
public record UndoableCounterState : Undoable<UndoableCounterState, CounterState>;
public sealed record CounterState(int ClickCount);
public sealed record UndoableCounterState : Undoable<UndoableCounterState, CounterState>;

public sealed class UndoableCounterFeature : Feature<UndoableCounterState>
{
Expand All @@ -88,8 +90,8 @@ public sealed class UndoableCounterFeature : Feature<UndoableCounterState>
}

// Or when net6:
public record CounterState(int ClickCount);
public record UndoableCounterState(CounterState Present) : Undoable<UndoableCounterState, CounterState>(Present);
public sealed record CounterState(int ClickCount);
public sealed record UndoableCounterState(CounterState Present) : Undoable<UndoableCounterState, CounterState>(Present);

public sealed class UndoableCounterFeature : Feature<UndoableCounterState>
{
Expand Down Expand Up @@ -119,7 +121,7 @@ to


```csharp
public class Reducers : UndoableStateReducers<UndoableCounterState>
public class Reducers : UndoableReducers<UndoableCounterState>
{
[ReducerMethod]
public static UndoableCounterState ReduceIncrementCounterAction(UndoableCounterState state, IncrementCounterAction action)
Expand All @@ -130,6 +132,7 @@ public class Reducers : UndoableStateReducers<UndoableCounterState>
}
```


**3) Update your injected IState properties**
Change setting of properties in your Razor pages from
```csharp
Expand Down Expand Up @@ -166,12 +169,40 @@ to

Also see example project in solution. Here both the Fluxor counter as Fluxor.Undo counter are implemented.


## Available undo/redo Actions
```csharp
Dispatcher.Dispatch(new UndoAction<T>()); // undo the last action
Dispatcher.Dispatch(new UndoAllAction<T>()); // undo all actions
Dispatcher.Dispatch(new RedoActionX<T>()); // redo the last action
Dispatcher.Dispatch(new RedoAllAction<T>()); // redo all actions
Dispatcher.Dispatch(new JumpAction<T>(-2)); // undo 2 steps
Dispatcher.Dispatch(new JumpAction<T>(5)); // redo 5 steps
```

## Helper methods on state
```csharp
public sealed record CounterState(int ClickCount);
public sealed record UndoableCounterState : Undoable<UndoableCounterState, CounterState>;
var state = new UndoableCounterState { Present = new CounterState { ClickCount = 0}};

var newState1 = state.WithNewPresent(p => p with { ClickCount = p.ClickCount + 1 }); // Moves current present to past and sets new present
var newState2 = state.WithNewPresent(new CounterState { ClickCount = 1}); // Moves current present to past and sets new present
var newState3 = state.WithInlineEditedPresent(p => p with { ClickCount = p.ClickCount + 1 }); // Doest NOT move current present to past and replaces current present
var newState4 = state.WithInlineEditedPresent(new CounterState { ClickCount = 1}); // Doest NOT move current present to past and replaces current present
```
## Tips
1) When you are allowing undo/redo, the undo/redo is done on the client side. So make sure that user knows that undo-ing does not alter data on server. There is a basic implementation in the example project in solution; page: Fluxor.Undo (Persist). Can be used as inspiration!
![Demo](https://raw.githubusercontent.com/Pjotrtje/Fluxor.Undo/main/docs/persist_demo.gif)
2) If you are using net6; upgrade to net7 so you can use the parameterless ctors and use the required properties :).


## Release notes
See the [Releases page](https://github.com/Pjotrtje/Fluxor.Undo/releases/).


## Versioning
Fluxor.Undo follows [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html) for the releases published to [nuget.org](https://www.nuget.org/).
Binary file added docs/persist_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/undo_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace BlazorClient.Features.Counter.Store;

[FeatureState(Name = "Counter", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record CounterState(int ClickCount)
public sealed record CounterState(int ClickCount)
{
public static CounterState CreateInitialState()
=> new(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace BlazorClient.Features.Counter.Store;

public record IncrementCounterAction(int Amount);
public sealed record IncrementCounterAction(int Amount);
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

namespace BlazorClient.Features.UndoableCounter.Store;

public record CounterState(int ClickCount);
public sealed record CounterState(int ClickCount);

[FeatureState(Name = "UndoableCounter", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
public sealed record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
{
public static UndoableCounterState CreateInitialState()
=> new() { Present = new(0) };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace BlazorClient.Features.UndoableCounter.Store;

public record IncrementCounterAction(int Amount);
public sealed record IncrementCounterAction(int Amount);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace BlazorClient.Features.UndoableCounter.Store;

public class Reducers : UndoableStateReducers<UndoableCounterState>
public class Reducers : UndoableReducers<UndoableCounterState>
{
[ReducerMethod]
public static UndoableCounterState ReduceIncrementCounterAction(UndoableCounterState state, IncrementCounterAction action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

namespace BlazorClient.Features.UndoableCounterWithPersistence.Store;

public record CounterState(int ClickCount);
public sealed record CounterState(int ClickCount);

[FeatureState(Name = "UndoableCounterWithPersistence", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
public sealed record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
{
public required CounterState Persisted { get; init; }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace BlazorClient.Features.UndoableCounterWithPersistence.Store;

public record IncrementCounterAction(int Amount);
public sealed record IncrementCounterAction(int Amount);
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace BlazorClient.Features.UndoableCounterWithPersistence.Store;

public record PersistAction;
public sealed record PersistAction;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace BlazorClient.Features.UndoableCounterWithPersistence.Store;

public class Reducers : UndoableStateReducers<UndoableCounterState>
public class Reducers : UndoableReducers<UndoableCounterState>
{
[ReducerMethod]
public static UndoableCounterState ReduceIncrementCounterAction(UndoableCounterState state, IncrementCounterAction action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<button class="btn btn-secondary" @onclick=@(() => Dispatcher.Dispatch(new UndoAction<UndoableCounterState>())) disabled="@UndoableCounterState.Value.HasNoPast">&lt;</button>
<button class="btn btn-secondary" @onclick=@(() => Dispatcher.Dispatch(new RedoAction<UndoableCounterState>())) disabled="@UndoableCounterState.Value.HasNoFuture">&gt;</button>
<button class="btn btn-secondary" @onclick=@(() => Dispatcher.Dispatch(new RedoAllAction<UndoableCounterState>())) disabled="@UndoableCounterState.Value.HasNoFuture">&gt;&gt;</button>

<!--In a real world app this dispatch of PersistAction should be done in an effect I guess.-->
<button class="btn btn-primary" @onclick=@(() => Dispatcher.Dispatch(new PersistAction())) disabled="@UndoableCounterState.Value.IsPersisted">Save</button>


Expand Down
2 changes: 1 addition & 1 deletion examples/BlazorClient/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"ReduxDevToolsTutorial": {
"BlazorClient": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
Expand Down
4 changes: 2 additions & 2 deletions src/Fluxor.Undo/Undoable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ private protected Undoable()
}

#if NET7_0_OR_GREATER
public record Undoable<TUndoable, TState> : Undoable<TUndoable>
public abstract record Undoable<TUndoable, TState> : Undoable<TUndoable>
where TUndoable : Undoable<TUndoable, TState>
where TState : notnull
{
public required TState Present { get; init; }
#else

public record Undoable<TUndoable, TState>(TState Present) : Undoable<TUndoable>
public abstract record Undoable<TUndoable, TState>(TState Present) : Undoable<TUndoable>
where TUndoable : Undoable<TUndoable, TState>
where TState : notnull
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Fluxor.Undo;

public abstract class UndoableStateReducers<TUndoable>
public abstract class UndoableReducers<TUndoable>
where TUndoable : Undoable<TUndoable>
{
[ReducerMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Fluxor.Undo.Tests.SmokeTestConfiguration;

public record CounterState(int ClickCount);
public sealed record CounterState(int ClickCount);

[FeatureState(Name = "UndoableCounter", CreateInitialStateMethodName = nameof(CreateInitialState))]
public record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
public sealed record UndoableCounterState : Undoable<UndoableCounterState, CounterState>
{
public static UndoableCounterState CreateInitialState()
=> new() { Present = new(0) };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Fluxor.Undo.Tests.SmokeTestConfiguration;

public record IncrementCounterAction(int Amount);
public sealed record IncrementCounterAction(int Amount);
2 changes: 1 addition & 1 deletion tests/Fluxor.Undo.Tests/SmokeTestConfiguration/Reducers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Fluxor.Undo.Tests.SmokeTestConfiguration;

public class Reducers : UndoableStateReducers<UndoableCounterState>
public class Reducers : UndoableReducers<UndoableCounterState>
{
[ReducerMethod]
public static UndoableCounterState ReduceIncrementCounterAction(UndoableCounterState state, IncrementCounterAction action)
Expand Down
4 changes: 2 additions & 2 deletions tests/Fluxor.Undo.Tests/Utils/UndoableIntState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ namespace Fluxor.Undo.Tests.Utils;

#if NET7_0_OR_GREATER

public record UndoableIntState : Undoable<UndoableIntState, int>;
public sealed record UndoableIntState : Undoable<UndoableIntState, int>;

#else

public record UndoableIntState(int Present)
public sealed record UndoableIntState(int Present)
: Undoable<UndoableIntState, int>(Present);

#endif

0 comments on commit 8da5fff

Please sign in to comment.