Skip to content

Commit

Permalink
chore: refactor DialogService
Browse files Browse the repository at this point in the history
  • Loading branch information
dansiegel committed Feb 28, 2024
1 parent 87a65f3 commit 2cf0ffe
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 187 deletions.
191 changes: 4 additions & 187 deletions src/Maui/Prism.Maui/Dialogs/DialogService.cs
Original file line number Diff line number Diff line change
@@ -1,209 +1,26 @@
using Prism.Commands;
using Prism.Common;
using Prism.Dialogs.Xaml;
using Prism.Mvvm;
using Prism.Navigation;

#nullable enable
namespace Prism.Dialogs;

/// <summary>
/// Provides the ability to display dialogs from ViewModels.
/// Provides a default scoped implementation of the <see cref="IDialogService"/>.
/// </summary>
public sealed class DialogService : IDialogService
public sealed class DialogService : DialogServiceBase
{
private readonly IContainerProvider _container;
private readonly IPageAccessor _pageAccessor;

/// <summary>
/// Creates a new instance of the <see cref="DialogService"/> for Maui Applications
/// </summary>
/// <param name="container">The <see cref="IContainerProvider"/> that will be used to help resolve the Dialog Views.</param>
/// <param name="pageAccessor">The <see cref="IPageAccessor"/> used to determine where in the Navigation Stack we need to process the Dialog.</param>
/// <exception cref="ArgumentNullException">Throws when any constructor arguments are null.</exception>
public DialogService(IContainerProvider container, IPageAccessor pageAccessor)
public DialogService(IPageAccessor pageAccessor)
{
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(pageAccessor);
_container = container;
_pageAccessor = pageAccessor;
}

/// <inheritdoc/>
public void ShowDialog(string name, IDialogParameters parameters, DialogCallback callback)
{
IDialogContainer? dialogModal = null;
try
{
parameters = UriParsingHelper.GetSegmentParameters(name, parameters ?? new DialogParameters());

// This needs to be resolved when called as a Module could load any time
// and register new dialogs
var registry = _container.Resolve<IDialogViewRegistry>();
var view = registry.CreateView(_container, UriParsingHelper.GetSegmentName(name)) as View
?? throw new ViewCreationException(name, ViewType.Dialog);

var currentPage = _pageAccessor.Page;
dialogModal = _container.Resolve<IDialogContainer>();
IDialogContainer.DialogStack.Add(dialogModal);
var dialogAware = GetDialogController(view);

async Task DialogAware_RequestClose(IDialogResult outResult)
{
bool didCloseDialog = true;
try
{
var result = await CloseDialogAsync(outResult ?? new DialogResult(), currentPage, dialogModal);
if (result.Exception is DialogException de && de.Message == DialogException.CanCloseIsFalse)
{
didCloseDialog = false;
return;
}

await callback.Invoke(result);
GC.Collect();
}
catch (DialogException dex)
{
if (dex.Message == DialogException.CanCloseIsFalse)
{
didCloseDialog = false;
return;
}

var result = new DialogResult
{
Exception = dex,
Parameters = parameters,
Result = ButtonResult.None
};

if (dex.Message != DialogException.CanCloseIsFalse)
{
await DialogService.InvokeError(callback, dex, parameters);
}
}
catch (Exception ex)
{
await DialogService.InvokeError(callback, ex, parameters);
}
finally
{
if (didCloseDialog && dialogModal is not null)
{
IDialogContainer.DialogStack.Remove(dialogModal);
}
}
}

DialogUtilities.InitializeListener(dialogAware, DialogAware_RequestClose);

dialogAware.OnDialogOpened(parameters);

if (!parameters.TryGetValue<bool>(KnownDialogParameters.CloseOnBackgroundTapped, out var closeOnBackgroundTapped))
{
var dialogLayoutCloseOnBackgroundTapped = DialogLayout.GetCloseOnBackgroundTapped(view);
if (dialogLayoutCloseOnBackgroundTapped.HasValue)
{
closeOnBackgroundTapped = dialogLayoutCloseOnBackgroundTapped.Value;
}
}

var dismissCommand = new DelegateCommand(() => dialogAware.RequestClose.Invoke(), dialogAware.CanCloseDialog);

PageNavigationService.NavigationSource = PageNavigationSource.DialogService;
dialogModal.ConfigureLayout(_pageAccessor.Page, view, closeOnBackgroundTapped, dismissCommand, parameters);
PageNavigationService.NavigationSource = PageNavigationSource.Device;

MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(currentPage, aa => aa.IsActive = false);
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(view, aa => aa.IsActive = true);
}
catch (Exception ex)
{
callback.Invoke(ex);
}
}

private static async Task InvokeError(DialogCallback callback, Exception exception, IDialogParameters parameters)
{
var result = new DialogResult
{
Parameters = parameters,
Exception = exception,
Result = ButtonResult.None
};
await callback.Invoke(result);
}

private static async Task<IDialogResult> CloseDialogAsync(IDialogResult result, Page currentPage, IDialogContainer dialogModal)
{
try
{
PageNavigationService.NavigationSource = PageNavigationSource.DialogService;

result ??= new DialogResult();
if (result.Parameters is null)
{
result = new DialogResult
{
Exception = result.Exception,
Parameters = new DialogParameters(),
Result = result.Result
};
}

var view = dialogModal.DialogView;
var dialogAware = GetDialogController(view);

if (!dialogAware.CanCloseDialog())
{
throw new DialogException(DialogException.CanCloseIsFalse);
}

PageNavigationService.NavigationSource = PageNavigationSource.DialogService;
await dialogModal.DoPop(currentPage);
PageNavigationService.NavigationSource = PageNavigationSource.Device;

MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(view, aa => aa.IsActive = false);
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(currentPage, aa => aa.IsActive = true);
dialogAware.OnDialogClosed();

return result;
}
catch (DialogException)
{
throw;
}
catch (Exception ex)
{
return new DialogResult
{
Exception = ex,
Parameters = result.Parameters,
Result = result.Result
};
}
finally
{
PageNavigationService.NavigationSource = PageNavigationSource.Device;
}
}

private static IDialogAware GetDialogController(View view)
{
if (view is IDialogAware viewAsDialogAware)
{
return viewAsDialogAware;
}
else if (view.BindingContext is null)
{
throw new DialogException(DialogException.NoViewModel);
}
else if (view.BindingContext is IDialogAware dialogAware)
{
return dialogAware;
}

throw new DialogException(DialogException.ImplementIDialogAware);
}
protected override Page? GetCurrentPage() => _pageAccessor.Page;
}
Loading

0 comments on commit 2cf0ffe

Please sign in to comment.