Kevin Deery
Kevin Deery

Reputation: 85

Unable to Navigate back to main page

Extremely frustrated with Shell Navigation, hope someone can help

Flow

  1. App loads and the Landing Page appears (this is the main page)
  2. The user taps the account icon and is routed to Login
  3. User enters their details and clicks the login button (i want to navigate back to Landing Page)

App Shell XAML

<ShellContent
   Title="LandingPage"
   ContentTemplate="{DataTemplate local:LandingPage}"
   Route="LandingPage" />
</Shell>

AppShell.xaml.cs

public AppShell()
{
    InitializeComponent();

    We dont register this route because its already defined in xaml
    // Routing.RegisterRoute(nameof(LandingPage), typeof(LandingPage));

    Routing.RegisterRoute(nameof(StoreItems), typeof(StoreItems));
    Routing.RegisterRoute(nameof(Register), typeof(Register));
    Routing.RegisterRoute(nameof(PasswordRecovery), typeof(PasswordRecovery));
    Routing.RegisterRoute(nameof(NewStoreRequest), typeof(NewStoreRequest));
    Routing.RegisterRoute(nameof(AccountInfo), typeof(AccountInfo));
    Routing.RegisterRoute(nameof(AllPromotions), typeof(AllPromotions));
    Routing.RegisterRoute(nameof(AllStores), typeof(AllStores));
    Routing.RegisterRoute(nameof(ChangePassword), typeof(ChangePassword));
    Routing.RegisterRoute(nameof(Checkout), typeof(Checkout));
    Routing.RegisterRoute(nameof(CreateAddress), typeof(CreateAddress));
    Routing.RegisterRoute(nameof(TrackOrder), typeof(TrackOrder));
    Routing.RegisterRoute(nameof(TrackOrderDetails), typeof(TrackOrderDetails));
    Routing.RegisterRoute(nameof(StoreBasket), typeof(StoreBasket));
    Routing.RegisterRoute(nameof(ProcessingPayment), typeof(ProcessingPayment));
    Routing.RegisterRoute(nameof(PaymentConfirmed), typeof(PaymentConfirmed));
    Routing.RegisterRoute(nameof(Login), typeof(Login));
}

MauiProgram.cs (UPDATED)

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureSyncfusionCore()
            .UseMauiCommunityToolkit()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("FontAwesome6BrandsRegular400.otf", "BrandsRegular");
                fonts.AddFont("FontAwesome6DuotoneSolid900.otf", "DuoToneSolid");
                fonts.AddFont("FontAwesome6ProLight300.otf", "ProLight");
                fonts.AddFont("FontAwesome6ProRegular.otf", "ProRegular");
                fonts.AddFont("FontAwesome6ProSolid900.otf", "ProSolid");
                fonts.AddFont("FontAwesome6ProThin100.otf", "ProThin");

                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        // Services 
        builder.Services.AddSingleton<INippyDataService, NippyDataService>();

        // Views 
        builder.Services.AddTransient<LandingPage>();
        builder.Services.AddTransient<StoreItems>();
        builder.Services.AddSingleton<Login>();
        builder.Services.AddSingleton<Register>();
        builder.Services.AddSingleton<PasswordRecovery>();
        builder.Services.AddTransient<AccountInfo>();
        builder.Services.AddTransient<AllPromotions>();
        builder.Services.AddTransient<AllStores>();
        builder.Services.AddSingleton<ChangePassword>();
        builder.Services.AddTransient<Checkout>();
        builder.Services.AddTransient<CreateAddress>();
        builder.Services.AddSingleton<NewStoreRequest>();
        builder.Services.AddTransient<TrackOrder>();
        builder.Services.AddTransient<TrackOrderDetails>();
        builder.Services.AddTransient<StoreBasket>();
        builder.Services.AddTransient<ProcessingPayment>();
        builder.Services.AddTransient<PaymentConfirmed>();

        // View Models
        builder.Services.AddTransient<LandingPageVM>();
        builder.Services.AddTransient<StoreItemsVM>();
        builder.Services.AddSingleton<LoginVM>();
        builder.Services.AddSingleton<RegisterVM>();
        builder.Services.AddSingleton<PasswordRecoveryVM>();
        builder.Services.AddTransient<AccountInfoVM>();
        builder.Services.AddTransient<AllPromotionsVM>();
        builder.Services.AddTransient<AllStoresVM>();
        builder.Services.AddSingleton<ChangePasswordVM>();
        builder.Services.AddTransient<CheckoutVM>();
        builder.Services.AddTransient<CreateAddressVM>();
        builder.Services.AddSingleton<NewStoreRequestVM>();
        builder.Services.AddTransient<TrackOrderVM>();
        builder.Services.AddTransient<TrackOrderDetailsVM>();
        builder.Services.AddTransient<StoreBasketVM>();
        builder.Services.AddTransient<ProcessingPaymentVM>();
        builder.Services.AddTransient<PaymentConfirmedVM>();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

Ive tried these

await Shell.Current.GoToAsync("..", true);  (Hangs and eventually times out)
await Shell.Current.GoToAsync(nameof(LandingPage), true); (Relative routing error try ..// etc)

Crazy thing, back from a different page

LandingPage -> AllStores

await Shell.Current.GoToAsync("..", true);

Its takes me to CreateAddress???

** Updated ** (Account button click code that takes the user to login)

 [RelayCommand]
        async Task BtnAccount()
        {

            if (CurrentUser != null)
            {
                await Shell.Current.GoToAsync(nameof(AccountInfo), true);
            }
            else
            {
                bool result = await Application.Current.MainPage.DisplayAlert("Account Required", $"You must be logged in to view Account details, do you wish to login now?", "Yes", "No");
                if (result)
                {
                    await Shell.Current.GoToAsync(nameof(Login), true);
                }
            }
        }

Login Button click

 [RelayCommand]
        async Task Login()
        {

            if (IsBusy)
                return;

            try
            {
                IsBusy = true;

                if (ValidateFormData())
                {
                    User user = await _dataService.LoginUserAsync(Email, Password);
                    if (user != null)
                    {
                        user.Order.Charity = await _dataService.GetCharityAsync();

                        if (user.ContactDetails.Email != null)
                        {
                            SharedSettings.SetCurrentUser(user);
                            if (!user.ChangePassword)
                            {
                                try
                                {
                                    var p = Shell.Current.Navigation.NavigationStack;
                                    await Shell.Current.GoToAsync(nameof(LandingPage), true);
                                }
                                catch (Exception ex) 
                                {
                                    var p = ex.Message;
                                }
                             

                            }
                            else
                            {
                                await Shell.Current.GoToAsync(nameof(ChangePassword), true);
                            }
                        }
                        else
                        {
                            await Shell.Current.DisplayAlert("Error!", "Login Failed", "OK");
                        }
                    }
                    else
                    {
                        await Shell.Current.DisplayAlert("Error!", $"Login Failed, account not found", "OK");
                    }

                    
                }
                else
                {
                    await Shell.Current.DisplayAlert("Error!", ErrorMessage, "OK");
                }

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                await Shell.Current.DisplayAlert("Error!", $"Service is not responding, please try again later", "OK");
            }
            finally
            {
                IsBusy = false;
            }
     
           
        }

UPDATED The navigation stack in postion 0 is null and position 1 is the login page. I have change the navigation to use nameof(LandingPage) to create a new instance of the page instead of the ".." (pop) method however im still getting the hang and time out

Upvotes: 1

Views: 604

Answers (3)

Kevin Deery
Kevin Deery

Reputation: 85

Problem solved

The issue is not with navigation rather a deadlock situation when the LandingPage is loading after a user logs in. The below http method caused the hang.

Adding .ConfigureAwait(false) to the call resolved it. Info: https://devblogs.microsoft.com/dotnet/configureawait-faq/

    public async Task<List<Address>> GetUserAddressesAsync(string email)
    {
        List<Address> data = new List<Address>();
        if (WebHelper.HasInternetConnection())
        {
            try
            {
                HttpResponseMessage response = await _httpClient.GetAsync($"{_api}/address/GetUserAddresses.php?email={email}");
                string json = await response.Content.ReadAsStringAsync();
                data = JsonConvert.DeserializeObject<List<Address>>(json);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("----> " + ex.Message);
            }
        }

        return data;
    } 

Thanks everyone for the help, 3 days of pain to get moving again.

Upvotes: 0

Kevin Deery
Kevin Deery

Reputation: 85

Turns out the Create Address page was corrupted somehow. Navigating to a different page than Login and attempting to navigate back using GoToAsync("..",true) kept taking me to Create Address.

I looked over everything on this page, including the code behind and View Model and i couldnt fault it. Once it was removed (Backup and ill create it again), i was able to Navigate from login back to the LandingPage using GoToAsync("..",true)

Upvotes: 0

H.A.H.
H.A.H.

Reputation: 3897

  1. Make all pages transient.

  2. You can navigate to your ShellContents, and keep them the only element on the stack, you cannot do this to routes however.

This is a problem in your case, 9/10 you will want login to be the only page in the stack. And then again, the shell main page, would be naturally another root.

So you need them both registered at the same level. (The top most is the entry)

Fix this, if you still have problems, ask.

Edit: To clarify a bit. You will want to jump between with "//MainPage" , "//LoginPage" to clear the stack.

Upvotes: 0

Related Questions