Skip to content
55 changes: 47 additions & 8 deletions Float.Core/UX/Coordinator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using static Float.Core.UX.ICoordinator;
#if NETSTANDARD
using Xamarin.Forms;
#else
Expand Down Expand Up @@ -59,6 +60,14 @@ public abstract class Coordinator : ICoordinator
/// <value><c>true</c> if is finished; otherwise, <c>false</c>.</value>
protected bool IsFinished => isFinished;

/// <summary>
/// Gets or sets the event args that are pending a finish.
/// </summary>
/// <value>
/// The event args that are pending a finish.
/// </value>
protected EventArgs WaitingToFinishEventArgs { get; set; }

/// <inheritdoc />
public virtual void Start(INavigationContext context)
{
Expand Down Expand Up @@ -98,6 +107,36 @@ public virtual void Start()
isStarted = true;
}

/// <inheritdoc/>
public CoordinatorRequestFinishStatus RequestFinish(EventArgs args)
{
return HandleFinishRequested(this, args);
}

/// <summary>
/// Handles when a finish is requested.
/// </summary>
/// <param name="coordinator">The coordinator that has requested this coordinator to finish.</param>
/// <param name="eventArgs">The event args.</param>
/// <returns>A value indicating whether this finished.</returns>
public virtual CoordinatorRequestFinishStatus HandleFinishRequested(ICoordinator coordinator, EventArgs eventArgs)
{
if (this == coordinator)
{
if (managedPage == null)
{
Finish(eventArgs);
return CoordinatorRequestFinishStatus.FinishedImmediately;
}

WaitingToFinishEventArgs = eventArgs;
NavigationContext.Reset(false);
return CoordinatorRequestFinishStatus.PendingFinish;
}

return CoordinatorRequestFinishStatus.WillNotFinish;
}

/// <summary>
/// Returning a page here will allow the coordinator to automatically
/// manage itself based on the state of the UI.
Expand Down Expand Up @@ -131,6 +170,7 @@ protected virtual void Finish(EventArgs args)
}

managedPage = null;
WaitingToFinishEventArgs = null;

if (NavigationContext != null)
{
Expand Down Expand Up @@ -208,17 +248,16 @@ void HandleNavigation(object sender, NavigationEventArgs args)
switch (args.Type)
{
case NavigationEventArgs.NavigationType.Popped:
case NavigationEventArgs.NavigationType.Reset:
if (args.Page == managedPage && !IsFinished)
{
Finish(EventArgs.Empty);
}

break;
if (WaitingToFinishEventArgs == null)
{
Finish(EventArgs.Empty);
return;
}

case NavigationEventArgs.NavigationType.Reset:
if (args.Page != managedPage && !IsFinished)
{
Finish(EventArgs.Empty);
Finish(WaitingToFinishEventArgs);
}

break;
Expand Down
36 changes: 36 additions & 0 deletions Float.Core/UX/CoordinatorParent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Float.Core.Extensions;
using static Float.Core.UX.ICoordinator;

namespace Float.Core.UX
{
Expand Down Expand Up @@ -117,6 +118,35 @@ public override string ToString()
return $"[{GetType()}, Children: {string.Join(",", childCoordinators)}]";
}

/// <inheritdoc />
public override CoordinatorRequestFinishStatus HandleFinishRequested(ICoordinator coordinator, EventArgs eventArgs)
{
WaitingToFinishEventArgs = eventArgs;

CoordinatorRequestFinishStatus? status = null;

foreach (var eachChild in ChildCoordinators)
{
if (eachChild is Coordinator childCoordinator)
{
var freshStatus = childCoordinator.HandleFinishRequested(this, eventArgs);

if (status == null)
{
status = freshStatus;
}

// If every child finishes with the same status we can return that, otherwise unknown seems to be the best option.
if (freshStatus != status)
{
status = CoordinatorRequestFinishStatus.Unknown;
}
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EBusch We're probably missing a call to the base implementation here. When you add that, you may have an issue with Finish getting called twice:

Coordinator.HandleNavigation may call it and so will CoordinatorParent.HandleChildFinish -- it is not currently clear to me the best way to address that.

return status ?? base.HandleFinishRequested(coordinator, eventArgs);
}

/// <inheritdoc />
protected override void Finish(EventArgs args)
{
Expand Down Expand Up @@ -149,6 +179,12 @@ protected virtual void HandleChildFinish(object sender, EventArgs args)
{
RemoveChild(child);
}

if (!HasChildren && WaitingToFinishEventArgs != null)
{
Finish(WaitingToFinishEventArgs);
WaitingToFinishEventArgs = null;
}
}
}
}
33 changes: 33 additions & 0 deletions Float.Core/UX/ICoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,43 @@ public interface ICoordinator
/// </summary>
event EventHandler<EventArgs> Finished;

/// <summary>
/// An enum for the possible results of a coordinator's RequestFinish.
/// </summary>
public enum CoordinatorRequestFinishStatus
{
/// <summary>
/// The coordinator RequestFinish finished immediately.
/// </summary>
FinishedImmediately,

/// <summary>
/// The coordinator RequestFinish is pending a finish.
/// </summary>
PendingFinish,

/// <summary>
/// The coordinator RequestFinish will not complete.
/// </summary>
WillNotFinish,

/// <summary>
/// The coordinator RequestFinish's status is unknown.
/// </summary>
Unknown,
}

/// <summary>
/// Implementing classes should use this to start this coordinator.
/// </summary>
/// <param name="navigationContext">The navigation context for this coordinator.</param>
void Start(INavigationContext navigationContext);

/// <summary>
/// Requests that a coordinator will finish.
/// </summary>
/// <param name="eventArgs">The event args.</param>
/// <returns>A task, with a boolean indicating whether this did finish.</returns>
CoordinatorRequestFinishStatus RequestFinish(EventArgs eventArgs);
}
}