Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow a Coordinator to be requested to finish #39

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
10 changes: 9 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Pack
run: dotnet pack --configuration Release --no-restore
- name: Save Artifact
uses: actions/upload-artifact@v3
with:
name: nupkg
path: ./Float.Core/obj/Release/
retention-days: 5
- name: Test
run: dotnet test --configuration Release --no-build --verbosity normal --logger:"trx;"
run: dotnet test --configuration MAUI --no-restore -f net7.0 --no-build --verbosity normal --logger:"trx;"
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
Expand Down
2 changes: 1 addition & 1 deletion Float.Core.Tests/Float.Core.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1;net7.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>9.0</LangVersion>
<UseMaui>true</UseMaui>
Expand Down
25 changes: 20 additions & 5 deletions Float.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,42 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Float.Core", "Float.Core\Float.Core.csproj", "{0AF193E3-D9AF-4551-B853-177F15FDC639}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Float.Core.Tests", "Float.Core.Tests\Float.Core.Tests.csproj", "{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}"
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "Float.Core.Tests", "Float.Core.Tests\Float.Core.Tests.csproj", "{0302630F-9229-48C3-BF66-0A8E7384A3DF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
MAUI|Any CPU = MAUI|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Release|Any CPU.Build.0 = Release|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Release|Any CPU.Build.0 = Release|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.MAUI|Any CPU.ActiveCfg = MAUI|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.MAUI|Any CPU.Build.0 = MAUI|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Release|Any CPU.Build.0 = Release|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.MAUI|Any CPU.ActiveCfg = MAUI|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.StandardHeader = $1
$1.IncludeInNewFiles = False
$0.VersionControlPolicy = $2
$0.DotNetNamingPolicy = $3
$3.DirectoryNamespaceAssociation = PrefixedHierarchical
$0.TextStylePolicy = $4
$4.inheritsSet = null
$4.scope = text/x-csharp
$0.CSharpFormattingPolicy = $5
$5.scope = text/x-csharp
$0.TextStylePolicy = $6
$6.FileWidth = 80
$6.TabsToSpaces = True
$6.scope = text/plain
EndGlobalSection
EndGlobal
20 changes: 19 additions & 1 deletion Float.Core/Float.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2;net7.0</TargetFrameworks>
<AssemblyName>Float.Core</AssemblyName>
<AssemblyAuthor>Float</AssemblyAuthor>
<AssemblyDescription>Common utility code used by Float projects.</AssemblyDescription>
Expand All @@ -14,6 +14,7 @@
<ReleaseVersion>1.0.0</ReleaseVersion>
<Version></Version>
<UseMaui>true</UseMaui>
<Configurations>Release;MAUI;Debug</Configurations>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(SolutionDir)\stylecop.json" />
Expand All @@ -31,6 +32,23 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>readme.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>TRACE;RELEASE;</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'MAUI|AnyCPU' ">
<IntermediateOutputPath>obj\MAUI</IntermediateOutputPath>
<DebugSymbols>true</DebugSymbols>
<Optimize>false</Optimize>
<OutputPath>bin\MAUI</OutputPath>
<DefineConstants>TRACE;DEBUG;MAUI;</DefineConstants>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\MAUI\Float.Core.xml</DocumentationFile>
<NoWarn></NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Float.TinCan" Version="1.0.3.30" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
Expand Down
56 changes: 53 additions & 3 deletions Float.Core/UX/Coordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,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 +106,36 @@ public virtual void Start()
isStarted = true;
}

/// <inheritdoc/>
public ICoordinator.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>
protected virtual ICoordinator.CoordinatorRequestFinishStatus HandleFinishRequested(ICoordinator coordinator, EventArgs eventArgs)
{
if (this == coordinator)
{
if (managedPage == null)
{
Finish(eventArgs);
return ICoordinator.CoordinatorRequestFinishStatus.FinishedImmediately;
}

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

return ICoordinator.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 +169,7 @@ protected virtual void Finish(EventArgs args)
}

managedPage = null;
WaitingToFinishEventArgs = null;

if (NavigationContext != null)
{
Expand Down Expand Up @@ -210,15 +249,26 @@ void HandleNavigation(object sender, NavigationEventArgs args)
case NavigationEventArgs.NavigationType.Popped:
if (args.Page == managedPage && !IsFinished)
{
Finish(EventArgs.Empty);
if (WaitingToFinishEventArgs == null)
{
Finish(EventArgs.Empty);
return;
}

Finish(WaitingToFinishEventArgs);
}

break;

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

Finish(WaitingToFinishEventArgs);
}

break;
Expand Down
22 changes: 22 additions & 0 deletions Float.Core/UX/CoordinatorParent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,22 @@ public override string ToString()
return $"[{GetType()}, Children: {string.Join(",", childCoordinators)}]";
}

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

foreach (var eachChild in ChildCoordinators)
{
if (eachChild is Coordinator childCoordinator)
{
childCoordinator.RequestFinish(eventArgs);
}
}

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 base.HandleFinishRequested(coordinator, eventArgs);
}

/// <inheritdoc />
protected override void Finish(EventArgs args)
{
Expand Down Expand Up @@ -149,6 +165,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>
EBusch marked this conversation as resolved.
Show resolved Hide resolved
CoordinatorRequestFinishStatus RequestFinish(EventArgs eventArgs);
}
}
Loading