mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
1 Commits
v2024.3.3
...
tech-debt/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
664d8d1f2c |
45
src/App/Abstractions/INavigationService.cs
Normal file
45
src/App/Abstractions/INavigationService.cs
Normal 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; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) =>
|
||||
{
|
||||
|
||||
164
src/App/Services/NavigationService.cs
Normal file
164
src/App/Services/NavigationService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user