Skip to content

Commit

Permalink
Simplified incremental loading usage
Browse files Browse the repository at this point in the history
  • Loading branch information
xperiandri committed Oct 16, 2021
1 parent e3ca955 commit 0226d19
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 26 deletions.
24 changes: 22 additions & 2 deletions src/Elmish.Uno.Mobile/IncrementalLoadingCollection.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type IncrementalLoadingCollection<'t> =
inherit ObservableCollection<'t>

val has: unit -> bool
val load: uint * TaskCompletionSource<uint> -> unit
val load: uint * (uint -> unit) -> unit

new (hasMoreItems, loadMoreItems) =
{ inherit ObservableCollection<'t>();
Expand All @@ -32,8 +32,28 @@ type IncrementalLoadingCollection<'t> =

member this.LoadMoreItemsAsync (count) =
let tcs = TaskCompletionSource<uint>()
this.load (count, tcs)
this.load (count, fun count -> tcs.SetResult(count))
let mapToResult (actualCountTask: Task<uint>) =
LoadMoreItemsResult(Count = actualCountTask.Result)
tcs.Task.ContinueWith(mapToResult, TaskContinuationOptions.OnlyOnRanToCompletion).AsAsyncOperation()

//(async {
// let tcs = TaskCompletionSource<uint>()
// let count =
// try
// let! count = this.load (count, fun count -> tcs.SetResult(count))
// count
// with
// | :? Exception as ex ->
// tcs.SetException(ex)
// ex.Reraise ()
// let mapToResult (actualCountTask: Task<uint>) =
// LoadMoreItemsResult(Count = actualCountTask.Result)
// tcs.Task.ContinueWith(mapToResult, TaskContinuationOptions.OnlyOnRanToCompletion).AsAsyncOperation()
// let! count = load count
// this.load (count, tcs)
// let complete (actualCount: uint) = async {
// return LoadMoreItemsResult(Count = actualCountTask.Result)
// }
//} |> Async.StartAsTask()).ContinueWith(mapToResult, TaskContinuationOptions.OnlyOnRanToCompletion).AsAsyncOperation()

12 changes: 7 additions & 5 deletions src/Elmish.Uno.Uwp/IncrementalLoadingCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;

using Microsoft.FSharp.Core;

using Windows.Foundation;
using Windows.UI.Xaml.Data;

Expand All @@ -12,21 +14,21 @@ internal class IncrementalLoadingCollection<T> : ObservableCollection<T>, ISuppo
{

private readonly Func<bool> hasMoreItems;
private readonly Action<uint, TaskCompletionSource<uint>> loadMoreItems;
private readonly Action<uint, FSharpFunc<uint, Unit>> loadMoreItems;

public IncrementalLoadingCollection(Func<bool> hasMoreItems, Action<uint, TaskCompletionSource<uint>> loadMoreItems)
public IncrementalLoadingCollection(Func<bool> hasMoreItems, Action<uint, FSharpFunc<uint, Unit>> loadMoreItems)
{
this.hasMoreItems = hasMoreItems;
this.loadMoreItems = loadMoreItems;
}

public IncrementalLoadingCollection(IEnumerable<T> collection, Func<bool> hasMoreItems, Action<uint, TaskCompletionSource<uint>> loadMoreItems) : base(collection)
public IncrementalLoadingCollection(IEnumerable<T> collection, Func<bool> hasMoreItems, Action<uint, FSharpFunc<uint, Unit>> loadMoreItems) : base(collection)
{
this.hasMoreItems = hasMoreItems;
this.loadMoreItems = loadMoreItems;
}

public IncrementalLoadingCollection(List<T> list, Func<bool> hasMoreItems, Action<uint, TaskCompletionSource<uint>> loadMoreItems) : base(list)
public IncrementalLoadingCollection(List<T> list, Func<bool> hasMoreItems, Action<uint, FSharpFunc<uint, Unit>> loadMoreItems) : base(list)
{
this.hasMoreItems = hasMoreItems;
this.loadMoreItems = loadMoreItems;
Expand All @@ -39,7 +41,7 @@ public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
async Task<LoadMoreItemsResult> LoadMoreItemsAsync ()
{
var tcs = new TaskCompletionSource<uint>();
loadMoreItems(count, tcs);
loadMoreItems(count, FuncConvert.FromAction<uint>(c => tcs.SetResult(c)));
var actualCount = await tcs.Task;
return new LoadMoreItemsResult { Count = actualCount };
}
Expand Down
6 changes: 3 additions & 3 deletions src/Elmish.Uno.Uwp/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ string GetBindingType(Binding<TModel, TMsg> binding)
public override ViewModelBase<TSubModel, TSubMsg> Create<TSubModel, TSubMsg>(TSubModel initialModel, FSharpFunc<TSubMsg, Unit> dispatch, FSharpList<Binding<TSubModel, TSubMsg>> bindings, ElmConfig config, string propNameChain)
=> new ViewModel<TSubModel, TSubMsg>(initialModel, dispatch, bindings, config, propNameChain);

public override ObservableCollection<T> CreateCollection<T>(FSharpFunc<TModel, bool> hasMoreItems, FSharpFunc<Tuple<uint, TaskCompletionSource<uint>>, TMsg> loadMoreitems, System.Collections.Generic.IEnumerable<T> collection)
public override ObservableCollection<T> CreateCollection<T>(FSharpFunc<TModel, bool> hasMoreItems, FSharpFunc<Tuple<uint, FSharpFunc<uint, Unit>>, TMsg> loadMoreitems, System.Collections.Generic.IEnumerable<T> collection)
{
void LoadMoreitems(uint count, TaskCompletionSource<uint> tcs)
void LoadMoreitems(uint count, FSharpFunc<uint, Unit> complete)
{
var msg = loadMoreitems.Invoke(new Tuple<uint, TaskCompletionSource<uint>>(count, tcs));
var msg = loadMoreitems.Invoke(new Tuple<uint, FSharpFunc<uint, Unit>>(count, complete));
Dispatch.Invoke(msg);
}
return new IncrementalLoadingCollection<T>(collection, () => hasMoreItems.Invoke(this.currentModel), LoadMoreitems);
Expand Down
2 changes: 1 addition & 1 deletion src/Elmish.Uno/Binding.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ open Elmish
open System.Threading.Tasks

type internal HasMoreItems<'model> = 'model -> bool
type internal LoadMoreItems<'msg> = uint * TaskCompletionSource<uint> -> 'msg
type internal LoadMoreItems<'msg> = uint * (uint -> unit) -> 'msg
[<Struct>]
type internal IncrementalLoadingData<'model,'msg> =
| Static
Expand Down
13 changes: 13 additions & 0 deletions src/Samples/Samples/Exception.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[<AutoOpen>]
[<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
module System.Exception

open System
open System.Runtime.ExceptionServices

// Useful for reraising exceptions under an async{...} context
// See this for more details: https://github.com/fsharp/fslang-suggestions/issues/660
type Exception with
member __.Reraise () =
(ExceptionDispatchInfo.Capture __).Throw ()
Unchecked.defaultof<_>
52 changes: 37 additions & 15 deletions src/Samples/Samples/OneWaySeq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,59 @@ open System.Threading.Tasks
open FSharp.Collections.Immutable
open Elmish
open Elmish.Uno
open System


type Model =
{ OneWaySeqNumbers: int list
OneWayNumbers: int list
IncrementalLoadingNumbers: int FlatList }
IncrementalLoadingNumbers: int FlatList
IsLoading: bool }

let initial =
{ OneWaySeqNumbers = [ 1000..-1..1 ]
OneWayNumbers = [ 1000..-1..1 ]
IncrementalLoadingNumbers = [ 1..1..10 ] |> FlatList.ofSeq }
IncrementalLoadingNumbers = [ 1..1..10 ] |> FlatList.ofSeq
IsLoading = false }

let init () = initial
let init () = initial, Cmd.none

type Msg =
| AddOneWaySeqNumber
| AddOneWayNumber
| LoadMore of count: uint * tcs: TaskCompletionSource<uint>
| LoadMore of count: uint * complete: (uint -> unit)
| LoadedMore of allItems: FlatList<int>
| ErrorLoadingMore

let asyncLoadItems (complete: uint -> unit) (items: FlatList<int>) count = async {
try
try
let intCount = int count
let builder = items.ToBuilder()
let max = FlatList.last items
for i = max + 1 to max + intCount do
builder.Add(i)
return LoadedMore <| builder.ToImmutable ()
with _ ->
return ErrorLoadingMore
finally
// Must be called to complete Task and unblock UI to update
// Otherwise it will block loading async operaiton and it will never be called again
complete count
}

let update msg m =
match msg with
| AddOneWaySeqNumber -> { m with OneWaySeqNumbers = m.OneWaySeqNumbers.Head + 1 :: m.OneWaySeqNumbers }
| AddOneWayNumber -> { m with OneWayNumbers = m.OneWayNumbers.Head + 1 :: m.OneWayNumbers }
| LoadMore (count, tcs) ->
let intCount = int count
let builder = m.IncrementalLoadingNumbers.ToBuilder()
let max = FlatList.last m.IncrementalLoadingNumbers
for i = max + 1 to max + intCount do
builder.Add(i)
tcs.SetResult(count)
{ m with IncrementalLoadingNumbers = builder.ToImmutable() }
| AddOneWaySeqNumber -> { m with OneWaySeqNumbers = m.OneWaySeqNumbers.Head + 1 :: m.OneWaySeqNumbers }, Cmd.none
| AddOneWayNumber -> { m with OneWayNumbers = m.OneWayNumbers.Head + 1 :: m.OneWayNumbers }, Cmd.none
| LoadMore (count, complete) ->
// If error hapened earlier it may be worth to do someting with that instead
{ m with IsLoading = true }, // Display some hint (indetermined progress bar)
asyncLoadItems complete m.IncrementalLoadingNumbers count |> Cmd.OfAsync.result
| LoadedMore items -> { m with
IsLoading = false // Hide loading indicator
IncrementalLoadingNumbers = items }, Cmd.none
| ErrorLoadingMore -> m, Cmd.none // add error to model that will appear on IU

let bindings : Binding<Model, Msg> list = [
"OneWaySeqNumbers" |> Binding.oneWaySeq ((fun m -> m.OneWaySeqNumbers), (=), id)
Expand All @@ -49,7 +71,7 @@ let designModel = initial

[<CompiledName("Program")>]
let program =
Program.mkSimpleUno init update bindings
Program.mkProgramUno init update bindings
|> Program.withConsoleTrace

[<CompiledName("Config")>]
Expand Down
1 change: 1 addition & 0 deletions src/Samples/Samples/Samples.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="Exception.fs" />
<Compile Include="SingleCounter.fs" />
<Compile Include="OneWaySeq.fs" />
<Compile Include="SubModel.fs" />
Expand Down

0 comments on commit 0226d19

Please sign in to comment.