-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from nabond251/feature/new-example
Feature/new example
- Loading branch information
Showing
26 changed files
with
846 additions
and
243 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
34 changes: 34 additions & 0 deletions
34
examples/Rearch.Reactor.Example/Capsules/ContextCapsules.cs
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,34 @@ | ||
using Microsoft.Extensions.DependencyInjection; | ||
using ReactorData; | ||
using Rearch.Reactor.Example.Helpers; | ||
using Rearch.Types; | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Rearch.Reactor.Example.Capsules | ||
{ | ||
internal static class ContextCapsules | ||
{ | ||
/// Represents the <see cref="IModelContext"/> that contains the todos | ||
/// dataset. | ||
internal static async Task<IModelContext> ContextAsyncCapsule(ICapsuleHandle use) | ||
{ | ||
await Task.Delay(1000); | ||
return (await ServiceHelper.ServicesAsync).GetRequiredService<IModelContext>(); | ||
} | ||
|
||
/// Allows for the <see cref="ContextAsyncCapsule"/> to more easily be | ||
/// warmed up for use in <see cref="ContextCapsule"/>. | ||
internal static AsyncValue<IModelContext> ContextWarmUpCapsule(ICapsuleHandle use) | ||
{ | ||
var task = use.Invoke(ContextAsyncCapsule); | ||
return use.Task(task); | ||
} | ||
|
||
/// Acts as a proxy to the warmed-up <see cref="ContextAsyncCapsule"/>. | ||
internal static IModelContext ContextCapsule(ICapsuleHandle use) => | ||
use.Invoke(ContextWarmUpCapsule).GetData().UnwrapOrElse( | ||
() => throw new InvalidOperationException( | ||
"ContextWarmUpCapsule was not warmed up!")); | ||
} | ||
} |
134 changes: 134 additions & 0 deletions
134
examples/Rearch.Reactor.Example/Capsules/TodoCapsules.cs
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,134 @@ | ||
using ReactorData; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Rearch.Reactor.Example.Models; | ||
using static Rearch.Reactor.Example.Capsules.ContextCapsules; | ||
using Rearch.Types; | ||
using System.Reactive.Linq; | ||
using System.Collections.Specialized; | ||
|
||
namespace Rearch.Reactor.Example.Capsules; | ||
|
||
public static class TodoCapsules | ||
{ | ||
/// <summary> | ||
/// Provides a way to create/update and delete todos. | ||
/// </summary> | ||
/// <param name="use">Capsule handle.</param> | ||
/// <returns>Actions to add, update, or delete todos.</returns> | ||
internal static ( | ||
Action<Todo> AddTodo, | ||
Action<Todo> UpdateTodo, | ||
Action<Todo> DeleteTodo) | ||
TodoItemsManagerCapsule(ICapsuleHandle use) | ||
{ | ||
var context = use.Invoke(ContextCapsule); | ||
|
||
return ( | ||
AddTodo: todo => | ||
{ | ||
context.Add(todo); | ||
context.Save(); | ||
}, | ||
UpdateTodo: todo => | ||
{ | ||
context.Replace(todo, new Todo | ||
{ | ||
Id = todo.Id, | ||
Title = todo.Title, | ||
Description = todo.Description, | ||
Completed = todo.Completed | ||
}); | ||
context.Save(); | ||
}, | ||
DeleteTodo: todo => | ||
{ | ||
context.Delete(todo); | ||
context.Save(); | ||
} | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Represents the current filter to search with | ||
/// (<see cref="string.Empty"/> as a query string represents no current | ||
/// query). | ||
/// </summary> | ||
/// <param name="use">Capsule handle.</param> | ||
/// <returns>Filter data and actions.</returns> | ||
internal static ( | ||
TodoListFilter Filter, | ||
Action<string> SetQueryString, | ||
Action ToggleCompletionStatus) | ||
FilterCapsule(ICapsuleHandle use) | ||
{ | ||
var (query, setQuery) = use.State(string.Empty); | ||
var (completionStatus, setCompletionStatus) = use.State(false); | ||
return ( | ||
new TodoListFilter | ||
{ | ||
Query = query, | ||
CompletionStatus = completionStatus, | ||
}, | ||
setQuery, | ||
() => setCompletionStatus(!completionStatus) | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Represents the todos list using the filter from the | ||
/// <see cref="FilterCapsule"/>. | ||
/// </summary> | ||
/// <param name="use">Capsule handle.</param> | ||
/// <returns>Query of to-do list items.</returns> | ||
internal static IQuery<Todo> TodoQueryCapsule(ICapsuleHandle use) | ||
{ | ||
var context = use.Invoke(ContextCapsule); | ||
var (filter, _, _) = use.Invoke(FilterCapsule); | ||
var filterQuery = filter.Query; | ||
var completionStatus = filter.CompletionStatus; | ||
|
||
// When query is null/empty, it does not affect the search. | ||
var todosQuery = use.Memo( | ||
() => context.Query<Todo>( | ||
query => query | ||
.Where( | ||
todo => | ||
( | ||
todo.Title.Contains(filterQuery, StringComparison.InvariantCultureIgnoreCase) || | ||
( | ||
todo.Description != null && | ||
todo.Description.Contains(filterQuery, StringComparison.InvariantCultureIgnoreCase))) && | ||
todo.Completed == completionStatus) | ||
.OrderBy(todo => todo.Id)), | ||
[context, filterQuery, completionStatus]); | ||
|
||
return todosQuery; | ||
} | ||
|
||
internal static AsyncValue<List<Todo>> TodoListCapsule(ICapsuleHandle use) | ||
{ | ||
var todosQuery = use.Invoke(TodoQueryCapsule); | ||
|
||
var todosObservable = use.Memo( | ||
() => Observable | ||
.FromEventPattern( | ||
(EventHandler<NotifyCollectionChangedEventArgs> ev) => new NotifyCollectionChangedEventHandler(ev), | ||
ev => todosQuery.CollectionChanged += ev, | ||
ev => todosQuery.CollectionChanged -= ev) | ||
.Select(e => (e.Sender as IEnumerable<Todo>)!) | ||
.StartWith(todosQuery), | ||
[todosQuery]); | ||
var todosState = use.Observable(todosObservable); | ||
return todosState.Select(todos => todos.ToList()); | ||
} | ||
|
||
/// <summary> | ||
/// Represents the length of the <see cref="TodoListCapsule"/>. | ||
/// </summary> | ||
/// <param name="use">Capsule handle.</param> | ||
/// <returns>Length of todos.</returns> | ||
internal static AsyncValue<int> TodoListCountCapsule(ICapsuleHandle use) => | ||
use.Invoke(TodoListCapsule).Select(todos => todos.Count); | ||
} |
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,51 @@ | ||
using MauiReactor; | ||
using Rearch.Reactor.Example.Models; | ||
using Rearch.Reactor.Components; | ||
using static Rearch.Reactor.Example.Capsules.TodoCapsules; | ||
using Rearch.Reactor.Example.Pages; | ||
using System; | ||
|
||
namespace Rearch.Reactor.Example.Components; | ||
|
||
partial class Body : CapsuleConsumer | ||
{ | ||
public override VisualNode Render(ICapsuleHandle use) | ||
{ | ||
var (isSearching, setIsSearching) = use.State(false); | ||
|
||
var (AddTodo, _, _) = use.Invoke(TodoItemsManagerCapsule); | ||
|
||
var ( | ||
filter, | ||
_, | ||
toggleCompletionStatus) = | ||
use.Invoke(FilterCapsule); | ||
var completionStatus = filter.CompletionStatus; | ||
|
||
return NavigationPage( | ||
ContentPage( | ||
ToolbarItem(completionStatus ? | ||
"Complete" : | ||
"Incomplete") | ||
.OnClicked(toggleCompletionStatus), | ||
|
||
ToolbarItem("Search") | ||
.OnClicked(() => setIsSearching(!isSearching)), | ||
|
||
ToolbarItem("Create") | ||
.OnClicked(() => ShowCreateTodoDialogAsync( | ||
ContainerPage, AddTodo)), | ||
|
||
new TodoList() | ||
) | ||
.Title("rearch todos") | ||
); | ||
|
||
void ShowCreateTodoDialogAsync( | ||
MauiControls.Page? containerPage, | ||
Action<Todo> todoCreator) | ||
{ | ||
containerPage?.Navigation.PushModalAsync<CreateTodoPage, CreateTodoPageProps>(p => p.TodoCreator = todoCreator); | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
examples/Rearch.Reactor.Example/Components/GlobalWarmUps.cs
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 MauiReactor; | ||
using ReactorData; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Rearch.Reactor.Components; | ||
using Rearch.Types; | ||
using static Rearch.Reactor.Example.Capsules.ContextCapsules; | ||
|
||
namespace Rearch.Reactor.Example.Components; | ||
|
||
partial class GlobalWarmUps(VisualNode child) : CapsuleConsumer | ||
{ | ||
public override VisualNode Render(ICapsuleHandle use) | ||
{ | ||
return new List<AsyncValue<IModelContext>> | ||
{ | ||
use.Invoke(ContextWarmUpCapsule) | ||
} | ||
.ToWarmUpComponent( | ||
child: child, | ||
loading: ContentPage(Label("Loading...").Center()), | ||
errorBuilder: errors => | ||
ContentPage(VStack( | ||
children: errors | ||
.Select(error => Label(error.Error.ToString())) | ||
.ToArray()) | ||
.Center())); | ||
} | ||
} |
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,59 @@ | ||
using MauiReactor; | ||
using System; | ||
using Rearch.Reactor.Example.Models; | ||
using Rearch.Reactor.Components; | ||
using Microsoft.Maui.Controls; | ||
using static Rearch.Reactor.Example.Capsules.TodoCapsules; | ||
|
||
namespace Rearch.Reactor.Example.Components; | ||
|
||
internal class TodoItem(Todo item) : CapsuleConsumer | ||
{ | ||
public override VisualNode Render(ICapsuleHandle use) | ||
{ | ||
var title = item.Title; | ||
var description = item.Description; | ||
var id = item.Id; | ||
var completed = item.Completed; | ||
|
||
var (_, UpdateTodo, DeleteTodo) = use.Invoke(TodoItemsManagerCapsule); | ||
|
||
void Delete() => DeleteTodo(item); | ||
void ToggleCompletionStatus() | ||
{ | ||
item.Title = title; | ||
item.Description = description; | ||
item.Id = id; | ||
item.Completed = !completed; | ||
|
||
UpdateTodo(item); | ||
} | ||
|
||
return Frame( | ||
Grid("27, 27", "Auto, *", | ||
CheckBox() | ||
.IsChecked(item.Completed) | ||
.OnCheckedChanged(ToggleCompletionStatus) | ||
.GridRowSpan(2), | ||
Label(item.Title) | ||
.FormattedText(() => | ||
{ | ||
FormattedString formattedString = new FormattedString(); | ||
formattedString.Spans.Add(new Span | ||
{ | ||
Text = item.Title, | ||
FontAttributes = FontAttributes.Bold, | ||
}); | ||
return formattedString; | ||
}) | ||
.VCenter() | ||
.GridColumn(1), | ||
Label(item.Description) | ||
.VCenter() | ||
.GridRow(1) | ||
.GridColumn(1)), | ||
TapGestureRecognizer(ToggleCompletionStatus, 1), | ||
TapGestureRecognizer(Delete, 2) | ||
); | ||
} | ||
} |
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,43 @@ | ||
using System.Linq; | ||
using MauiReactor; | ||
using Rearch.Reactor.Example.Models; | ||
using Rearch.Reactor.Components; | ||
using static Rearch.Reactor.Example.Capsules.TodoCapsules; | ||
using Rearch.Types; | ||
|
||
namespace Rearch.Reactor.Example.Components; | ||
|
||
partial class TodoList : CapsuleConsumer | ||
{ | ||
public override VisualNode Render(ICapsuleHandle use) | ||
{ | ||
var completionFilter = use.Invoke(FilterCapsule).Filter.CompletionStatus; | ||
var completionText = completionFilter ? "completed" : "incomplete"; | ||
|
||
var todoListCount = use.Invoke(TodoListCountCapsule); | ||
var todoQuery = use.Invoke(TodoQueryCapsule); | ||
|
||
var todoListWidget = todoListCount.GetData().Select(_ => | ||
{ | ||
return CollectionView().ItemsSource( | ||
todoQuery, | ||
i => new TodoItem(i)); | ||
}).AsNullable(); | ||
|
||
var infoWidget = todoListCount.Match( | ||
onLoading: _ => Label("Loading..."), | ||
onError: (error, _) => Label(error.ToString()), | ||
onData: count => count == 0 ? | ||
Label($"No {completionText} todos found") : | ||
null); | ||
|
||
return StackLayout( | ||
children: (todoListWidget != null ? | ||
[todoListWidget] : | ||
Enumerable.Empty<VisualNode>()) | ||
.Concat(infoWidget != null ? | ||
[Frame(infoWidget)] : | ||
Enumerable.Empty<VisualNode>()) | ||
.ToArray()); | ||
} | ||
} |
Oops, something went wrong.