1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

...

1 Commits

Author SHA1 Message Date
Jacob Fink
664d8d1f2c create navigation service 2021-12-30 09:31:56 -05:00
3 changed files with 212 additions and 1 deletions

View File

@@ -0,0 +1,45 @@
using Bit.App.Pages;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Bit.App.Abstractions
{
public interface INavigationService
{
/// <summary>
/// Sets the viewmodel to be the main page of the application
/// </summary>
void PresentAsMainPage(BaseViewModel viewModel);
/// <summary>
/// Sets the viewmodel as the main page of the application, and wraps its page within a Navigation page
/// </summary>
void PresentAsNavigatableMainPage(BaseViewModel viewModel);
/// <summary>
/// Navigate to the given page on top of the current navigation stack
/// </summary>
Task NavigateTo(BaseViewModel viewModel);
/// <summary>
/// Navigate to the previous item in the navigation stack
/// </summary>
Task NavigateBack();
/// <summary>
/// Navigate back to the element at the root of the navigation stack
/// </summary>
Task NavigateBackToRoot();
}
public interface IViewLocator
{
Page CreateAndBindPageFor<TViewModel>(TViewModel viewModel) where TViewModel : BaseViewModel;
}
public interface IMainPage
{
Page MainPage { get; set; }
}
}

View File

@@ -15,7 +15,7 @@ using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Bit.App
{
public partial class App : Application
public partial class App : Application, IMainPage
{
private readonly IUserService _userService;
private readonly IBroadcasterService _broadcasterService;
@@ -51,6 +51,8 @@ namespace Bit.App
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
var navigator = new NavigationService(this, new ViewLocator());
Bootstrap();
_broadcasterService.Subscribe(nameof(App), async (message) =>
{

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Bit.App.Abstractions;
using Bit.App.Pages;
namespace Bit.App.Services
{
public class NavigationService : INavigationService
{
private readonly IMainPage _presentationRoot;
private readonly IViewLocator _viewLocator;
public NavigationService(IMainPage presentationRoot, IViewLocator viewLocator)
{
_presentationRoot = presentationRoot;
_viewLocator = viewLocator;
}
private Xamarin.Forms.INavigation Navigator => _presentationRoot.MainPage.Navigation;
public void PresentAsMainPage(BaseViewModel viewModel)
{
var page = _viewLocator.CreateAndBindPageFor(viewModel);
IEnumerable<BaseViewModel> viewModelsToDismiss = FindViewModelsToDismiss(_presentationRoot.MainPage);
if (_presentationRoot.MainPage is NavigationPage navPage)
{
// If we're replacing a navigation page, unsub from events
navPage.PopRequested -= NavPagePopRequested;
}
// viewModel.BeforeFirstShown();
_presentationRoot.MainPage = page;
foreach (BaseViewModel toDismiss in viewModelsToDismiss)
{
// toDismiss.AfterDismissed();
}
}
public void PresentAsNavigatableMainPage(BaseViewModel viewModel)
{
var page = _viewLocator.CreateAndBindPageFor(viewModel);
NavigationPage newNavigationPage = new NavigationPage(page);
IEnumerable<BaseViewModel> viewModelsToDismiss = FindViewModelsToDismiss(_presentationRoot.MainPage);
if (_presentationRoot.MainPage is NavigationPage navPage)
{
navPage.PopRequested -= NavPagePopRequested;
}
// viewModel.BeforeFirstShown();
// Listen for back button presses on the new navigation bar
newNavigationPage.PopRequested += NavPagePopRequested;
_presentationRoot.MainPage = newNavigationPage;
foreach (BaseViewModel toDismiss in viewModelsToDismiss)
{
// toDismiss.AfterDismissed();
}
}
private IEnumerable<BaseViewModel> FindViewModelsToDismiss(Page dismissingPage)
{
var viewmodels = new List<BaseViewModel>();
if (dismissingPage is NavigationPage)
{
viewmodels.AddRange(
Navigator
.NavigationStack
.Select(p => p.BindingContext)
.OfType<BaseViewModel>()
);
}
else
{
var viewmodel = dismissingPage?.BindingContext as BaseViewModel;
if (viewmodel != null) viewmodels.Add(viewmodel);
}
return viewmodels;
}
private void NavPagePopRequested(object sender, NavigationRequestedEventArgs e)
{
if (Navigator.NavigationStack.LastOrDefault()?.BindingContext is BaseViewModel poppingPage)
{
// poppingPage.AfterDismissed();
}
}
public async Task NavigateTo(BaseViewModel viewModel)
{
var page = _viewLocator.CreateAndBindPageFor(viewModel);
// await viewModel.BeforeFirstShown();
await Navigator.PushAsync(page);
}
public async Task NavigateBack()
{
var dismissing = Navigator.NavigationStack.Last().BindingContext as BaseViewModel;
await Navigator.PopAsync();
// dismissing?.AfterDismissed();
}
public async Task NavigateBackToRoot()
{
var toDismiss = Navigator
.NavigationStack
.Skip(1)
.Select(vw => vw.BindingContext)
.OfType<BaseViewModel>()
.ToArray();
await Navigator.PopToRootAsync();
foreach (BaseViewModel viewModel in toDismiss)
{
// viewModel.AfterDismissed().FireAndForget();
}
}
}
public class ViewLocator : IViewLocator
{
public Page CreateAndBindPageFor<TViewModel>(TViewModel viewModel) where TViewModel : BaseViewModel
{
var pageType = FindPageForViewModel(viewModel.GetType());
var page = (Page)Activator.CreateInstance(pageType);
page.BindingContext = viewModel;
return page;
}
protected virtual Type FindPageForViewModel(Type viewModelType)
{
var pageTypeName = viewModelType
.AssemblyQualifiedName
.Replace("ViewModel", "");
var pageType = Type.GetType(pageTypeName);
if (pageType == null)
throw new ArgumentException(pageTypeName + " type does not exist");
return pageType;
}
}
}