From b16a1bffc114ac52fb193370d1e9c702f4fe6fe7 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 11 Jun 2026 09:09:33 +0700 Subject: [PATCH 1/6] Add app actions and intent handling for Android, enable deep linking, and improve URL state persistence in FwLiteMaui --- backend/FwLite/FwLiteMaui/App.xaml.cs | 12 ++++--- backend/FwLite/FwLiteMaui/MainPage.xaml.cs | 32 +++++++++++++++++-- backend/FwLite/FwLiteMaui/MauiProgram.cs | 12 +++++++ .../Platforms/Android/MainActivity.cs | 14 ++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/backend/FwLite/FwLiteMaui/App.xaml.cs b/backend/FwLite/FwLiteMaui/App.xaml.cs index c4d2cc6b53..4c298ac389 100644 --- a/backend/FwLite/FwLiteMaui/App.xaml.cs +++ b/backend/FwLite/FwLiteMaui/App.xaml.cs @@ -1,4 +1,5 @@ using FwLiteShared.Services; +using Microsoft.AspNetCore.Components; namespace FwLiteMaui; @@ -9,14 +10,15 @@ public partial class App : Application public App(MainPage mainPage, IPreferencesService preferences) { _mainPage = mainPage; - var lastUrl = preferences.Get(nameof(PreferenceKey.AppLastUrl)); - if (lastUrl?.StartsWith('/') == true) - { - mainPage.StartPath = lastUrl; - } InitializeComponent(); } + internal void LoadAppUrl(string url) + { + _mainPage.LoadAppUrl(url); + + } + protected override Window CreateWindow(IActivationState? activationState) { return CreateWindow(_mainPage); diff --git a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs index f6b60aa407..0766d77897 100644 --- a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs +++ b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs @@ -1,3 +1,5 @@ +using FwLiteShared.Services; +using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebView; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -7,15 +9,27 @@ namespace FwLiteMaui; public partial class MainPage : ContentPage { - public MainPage() + public MainPage(IPreferencesService preferences) { InitializeComponent(); blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing; blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; blazorWebView.UrlLoading += BlazorWebViewOnUrlLoading; - } - + var lastUrl = preferences.Get(nameof(PreferenceKey.AppLastUrl)); + #if ANDROID + var intent = Platform.CurrentActivity?.Intent; + if (intent is not null && intent.Action == "ACTION_XE_APP_ACTION") + { + var actionId = intent.GetStringExtra("EXTRA_XE_APP_ACTION_ID"); + if (actionId is "home") lastUrl = "/home"; + } + #endif + if (lastUrl?.StartsWith('/') == true) + { + StartPath = lastUrl; + } + } internal string StartPath { @@ -23,6 +37,18 @@ internal string StartPath set => blazorWebView.StartPath = value; } + internal void LoadAppUrl(string url) + { + blazorWebView.StartPath = url; + _ = blazorWebView.TryDispatchAsync(services => + { + var navigationManager = services.GetRequiredService(); + navigationManager.NavigateTo(url); + //the svelte-routing library doesn't notice the url change via NavigateTo, so just reload the page + navigationManager.Refresh(); + }); + } + #if ANDROID || WINDOWS private partial void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e); private partial void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e); diff --git a/backend/FwLite/FwLiteMaui/MauiProgram.cs b/backend/FwLite/FwLiteMaui/MauiProgram.cs index 30630df99c..cdc6c33ca9 100644 --- a/backend/FwLite/FwLiteMaui/MauiProgram.cs +++ b/backend/FwLite/FwLiteMaui/MauiProgram.cs @@ -52,6 +52,18 @@ public static MauiApp CreateMauiApp() builder.ConfigureEssentials(essentialsBuilder => { essentialsBuilder.UseVersionTracking(); + + essentialsBuilder.AddAppAction("home", "Home") + .OnAppAction(action => + { + if (action.Id == "home") + { + App.Current!.Dispatcher.Dispatch(() => + { + ((App?)App.Current)!.LoadAppUrl("/home"); + }); + } + }); }); builder.Services.AddFwLiteMauiServices(builder.Configuration, builder.Logging); diff --git a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs index c8b0d36594..a7a731d1a7 100644 --- a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs +++ b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs @@ -14,6 +14,8 @@ namespace FwLiteMaui; LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] +[IntentFilter([Platform.Intent.ActionAppAction], + Categories = [Intent.CategoryDefault])] public class MainActivity : MauiAppCompatActivity { protected override void OnCreate(Bundle? savedInstanceState) @@ -22,6 +24,18 @@ protected override void OnCreate(Bundle? savedInstanceState) ApplyBrandedSystemBars(); } + protected override void OnResume() + { + base.OnResume(); + + Platform.OnResume(this); + } + + protected override void OnNewIntent(Intent? intent) + { + base.OnNewIntent(intent); + Platform.OnNewIntent(intent); + } public override void OnConfigurationChanged(Configuration newConfig) { base.OnConfigurationChanged(newConfig); From f75bca5a28e6bcf8a5aa14bf03cd27769baf6cb2 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 11 Jun 2026 15:20:01 +0700 Subject: [PATCH 2/6] Expose `SvelteNavigate` for native host integrations and improve shortcut handling in FwLiteMaui --- backend/FwLite/FwLiteMaui/App.xaml.cs | 6 +-- backend/FwLite/FwLiteMaui/MainPage.xaml.cs | 38 ++++++++++--------- backend/FwLite/FwLiteMaui/MauiProgram.cs | 23 ++++++----- .../Platforms/Android/MainActivity.cs | 11 +++++- backend/FwLite/FwLiteMaui/Shortcuts.cs | 37 ++++++++++++++++++ .../build/Linq2DbCctorPatcher/Program.cs | 7 ++++ .../src/lib/services/service-declaration.ts | 2 + frontend/viewer/src/main.ts | 4 ++ 8 files changed, 96 insertions(+), 32 deletions(-) create mode 100644 backend/FwLite/FwLiteMaui/Shortcuts.cs diff --git a/backend/FwLite/FwLiteMaui/App.xaml.cs b/backend/FwLite/FwLiteMaui/App.xaml.cs index 4c298ac389..0f400925a3 100644 --- a/backend/FwLite/FwLiteMaui/App.xaml.cs +++ b/backend/FwLite/FwLiteMaui/App.xaml.cs @@ -1,13 +1,12 @@ -using FwLiteShared.Services; -using Microsoft.AspNetCore.Components; namespace FwLiteMaui; public partial class App : Application { private readonly MainPage _mainPage; + public static string? OverrideStartupUrl { get; set; } - public App(MainPage mainPage, IPreferencesService preferences) + public App(MainPage mainPage) { _mainPage = mainPage; InitializeComponent(); @@ -16,7 +15,6 @@ public App(MainPage mainPage, IPreferencesService preferences) internal void LoadAppUrl(string url) { _mainPage.LoadAppUrl(url); - } protected override Window CreateWindow(IActivationState? activationState) diff --git a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs index 0766d77897..f339e7a72a 100644 --- a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs +++ b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.JSInterop; namespace FwLiteMaui; @@ -12,23 +13,18 @@ public partial class MainPage : ContentPage public MainPage(IPreferencesService preferences) { InitializeComponent(); + var lastUrlFromPrefs = preferences.Get(nameof(PreferenceKey.AppLastUrl)); blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing; blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; - blazorWebView.UrlLoading += BlazorWebViewOnUrlLoading; - - var lastUrl = preferences.Get(nameof(PreferenceKey.AppLastUrl)); - #if ANDROID - var intent = Platform.CurrentActivity?.Intent; - if (intent is not null && intent.Action == "ACTION_XE_APP_ACTION") + blazorWebView.BlazorWebViewInitialized += (s, e) => { - var actionId = intent.GetStringExtra("EXTRA_XE_APP_ACTION_ID"); - if (actionId is "home") lastUrl = "/home"; - } - #endif - if (lastUrl?.StartsWith('/') == true) - { - StartPath = lastUrl; - } + // Decide initial StartPath here (after Android intent is processed) to avoid cold-start race + var initial = App.OverrideStartupUrl ?? lastUrlFromPrefs ?? "/"; + //only change it if it's still the default, might have been changed already, for example when opening a new window + if (blazorWebView.StartPath == "/") + blazorWebView.StartPath = initial; + }; + blazorWebView.UrlLoading += BlazorWebViewOnUrlLoading; } internal string StartPath @@ -42,10 +38,16 @@ internal void LoadAppUrl(string url) blazorWebView.StartPath = url; _ = blazorWebView.TryDispatchAsync(services => { - var navigationManager = services.GetRequiredService(); - navigationManager.NavigateTo(url); - //the svelte-routing library doesn't notice the url change via NavigateTo, so just reload the page - navigationManager.Refresh(); + var jsRuntime = services.GetService(); + if (jsRuntime != null) + { + var js = $$""" + if (window.lexbox.SvelteNavigate) { + window.lexbox.SvelteNavigate('{{url}}', { replace: true }); + } + """; + _ = jsRuntime.InvokeVoidAsync("eval", js); + } }); } diff --git a/backend/FwLite/FwLiteMaui/MauiProgram.cs b/backend/FwLite/FwLiteMaui/MauiProgram.cs index cdc6c33ca9..66a82b0db3 100644 --- a/backend/FwLite/FwLiteMaui/MauiProgram.cs +++ b/backend/FwLite/FwLiteMaui/MauiProgram.cs @@ -53,17 +53,22 @@ public static MauiApp CreateMauiApp() { essentialsBuilder.UseVersionTracking(); - essentialsBuilder.AddAppAction("home", "Home") - .OnAppAction(action => + // Register all shortcuts from a single central place + foreach (var action in Shortcuts.Declarations) + { + essentialsBuilder.AddAppAction(action); + } + + essentialsBuilder.OnAppAction(action => + { + if (Shortcuts.TryGetUrl(action.Id, out var url)) { - if (action.Id == "home") + Application.Current!.Dispatcher.Dispatch(() => { - App.Current!.Dispatcher.Dispatch(() => - { - ((App?)App.Current)!.LoadAppUrl("/home"); - }); - } - }); + ((App?)Application.Current)?.LoadAppUrl(url); + }); + } + }); }); builder.Services.AddFwLiteMauiServices(builder.Configuration, builder.Logging); diff --git a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs index a7a731d1a7..0c006979a4 100644 --- a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs +++ b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs @@ -21,13 +21,22 @@ public class MainActivity : MauiAppCompatActivity protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); + + if (Intent?.Action == Platform.Intent.ActionAppAction) + { + var actionId = Intent.GetStringExtra("EXTRA_XE_APP_ACTION_ID"); + if (Shortcuts.TryGetUrl(actionId, out var url)) + { + App.OverrideStartupUrl = url; + } + } + ApplyBrandedSystemBars(); } protected override void OnResume() { base.OnResume(); - Platform.OnResume(this); } diff --git a/backend/FwLite/FwLiteMaui/Shortcuts.cs b/backend/FwLite/FwLiteMaui/Shortcuts.cs new file mode 100644 index 0000000000..755854aff8 --- /dev/null +++ b/backend/FwLite/FwLiteMaui/Shortcuts.cs @@ -0,0 +1,37 @@ +using System.Collections.ObjectModel; + +namespace FwLiteMaui; + +public static class Shortcuts +{ + // Define shortcut IDs here + public const string Home = "home"; + + // Map IDs to URL paths + private static readonly IReadOnlyDictionary IdToUrl = new Dictionary + { + [Home] = "/", + }; + + // Titles/subtitles shown in the system UI (if supported) + public static readonly IReadOnlyList Declarations = + [ + new(Home, "Home") + ]; + + public static bool TryGetUrl(string? id, out string url) + { + if (id is null) + { + url = string.Empty; + return false; + } + if (IdToUrl.TryGetValue(id, out var found)) + { + url = found; + return true; + } + url = string.Empty; + return false; + } +} diff --git a/backend/FwLite/FwLiteMaui/build/Linq2DbCctorPatcher/Program.cs b/backend/FwLite/FwLiteMaui/build/Linq2DbCctorPatcher/Program.cs index 03e1b416e4..df5bfa04d7 100644 --- a/backend/FwLite/FwLiteMaui/build/Linq2DbCctorPatcher/Program.cs +++ b/backend/FwLite/FwLiteMaui/build/Linq2DbCctorPatcher/Program.cs @@ -53,6 +53,13 @@ static int Fail(string message) if (cctor is null || !cctor.HasBody) return Fail("SqlTransparentExpression .cctor not found (or has no body)."); + if (cctor.Body.Instructions.Count == 1 && cctor.Body.Instructions[0].OpCode == OpCodes.Ret) + { + Console.WriteLine($"Already patched (inferred from IL): {dllPath}"); + File.WriteAllText(markerPath, DateTime.UtcNow.ToString("O")); + return 0; + } + // Sanity-check the cctor shape: at least one stsfld targeting the _ctor field. // If upstream renames _ctor or restructures the field init, we want to know. var storesCtorField = cctor.Body.Instructions.Any(ins => diff --git a/frontend/viewer/src/lib/services/service-declaration.ts b/frontend/viewer/src/lib/services/service-declaration.ts index 81f8cb1c55..5cdf5a3905 100644 --- a/frontend/viewer/src/lib/services/service-declaration.ts +++ b/frontend/viewer/src/lib/services/service-declaration.ts @@ -10,6 +10,8 @@ declare global { ServiceProvider: LexboxServiceProvider; Search: { openSearch: (search: string) => void }; IsDotnetHosted?: boolean; + // Expose svelte-routing navigate for native hosts (MAUI) and other integrations + SvelteNavigate?: (url: string, options?: { replace?: boolean; state?: unknown }) => void; /* eslint-enable @typescript-eslint/naming-convention */ } diff --git a/frontend/viewer/src/main.ts b/frontend/viewer/src/main.ts index c657072fee..c127e290e7 100644 --- a/frontend/viewer/src/main.ts +++ b/frontend/viewer/src/main.ts @@ -7,6 +7,7 @@ import '@formatjs/intl-durationformat/polyfill'; import App from './App.svelte'; import {mount} from 'svelte'; +import {navigate} from 'svelte-routing'; import {setupDotnetServiceProvider} from './lib/services/service-provider-dotnet'; import {setupServiceProvider} from '$lib/services/service-provider'; import {setupBrowserAppServices} from '$lib/services/browser-app-services'; @@ -20,6 +21,9 @@ if (!window.lexbox.IsDotnetHosted) { } useEventBus(); +// Wire up globally-accessible helpers for hosts (e.g., MAUI) +window.lexbox.SvelteNavigate = (url: string, options?: { replace?: boolean }) => navigate(url, options); + //don't mount the app until after we've loaded the local void setLanguage('default') .then(() => { From e9d99a474608651554cf03e0f9748fb02e8aea20 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 11 Jun 2026 15:53:51 +0700 Subject: [PATCH 3/6] mark the android activity as resizable to allow resizing on chromebooks --- backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs index 0c006979a4..82afe208b6 100644 --- a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs +++ b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs @@ -12,6 +12,7 @@ namespace FwLiteMaui; [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, + ResizeableActivity = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] [IntentFilter([Platform.Intent.ActionAppAction], From 2e2432c48b22a0521184e999e0c3d0a588f07374 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Thu, 11 Jun 2026 16:29:02 +0700 Subject: [PATCH 4/6] Resolve type inconsistency in `SvelteNavigate` definition, improve JSON serialization of URLs, and clean up unused imports. --- backend/FwLite/FwLiteMaui/MainPage.xaml.cs | 8 +++----- .../FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs | 1 + frontend/viewer/src/lib/services/service-declaration.ts | 2 +- frontend/viewer/src/main.ts | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs index f339e7a72a..11af842636 100644 --- a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs +++ b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs @@ -1,9 +1,6 @@ +using System.Text.Json; using FwLiteShared.Services; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebView; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Microsoft.JSInterop; namespace FwLiteMaui; @@ -23,6 +20,7 @@ public MainPage(IPreferencesService preferences) //only change it if it's still the default, might have been changed already, for example when opening a new window if (blazorWebView.StartPath == "/") blazorWebView.StartPath = initial; + App.OverrideStartupUrl = null; }; blazorWebView.UrlLoading += BlazorWebViewOnUrlLoading; } @@ -43,7 +41,7 @@ internal void LoadAppUrl(string url) { var js = $$""" if (window.lexbox.SvelteNavigate) { - window.lexbox.SvelteNavigate('{{url}}', { replace: true }); + window.lexbox.SvelteNavigate({{JsonSerializer.Serialize(url)}}, { replace: true }); } """; _ = jsRuntime.InvokeVoidAsync("eval", js); diff --git a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs index 82afe208b6..be23b14aa0 100644 --- a/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs +++ b/backend/FwLite/FwLiteMaui/Platforms/Android/MainActivity.cs @@ -25,6 +25,7 @@ protected override void OnCreate(Bundle? savedInstanceState) if (Intent?.Action == Platform.Intent.ActionAppAction) { + //name comes from internal maui code: https://github.com/dotnet/maui/blob/271d2505eb436600bb84002c8941670abb0ae23b/src/Essentials/src/AppActions/AppActions.android.cs#L83 var actionId = Intent.GetStringExtra("EXTRA_XE_APP_ACTION_ID"); if (Shortcuts.TryGetUrl(actionId, out var url)) { diff --git a/frontend/viewer/src/lib/services/service-declaration.ts b/frontend/viewer/src/lib/services/service-declaration.ts index 5cdf5a3905..42a63eade1 100644 --- a/frontend/viewer/src/lib/services/service-declaration.ts +++ b/frontend/viewer/src/lib/services/service-declaration.ts @@ -11,7 +11,7 @@ declare global { Search: { openSearch: (search: string) => void }; IsDotnetHosted?: boolean; // Expose svelte-routing navigate for native hosts (MAUI) and other integrations - SvelteNavigate?: (url: string, options?: { replace?: boolean; state?: unknown }) => void; + SvelteNavigate?: (url: string, options?: { replace?: boolean }) => void; /* eslint-enable @typescript-eslint/naming-convention */ } diff --git a/frontend/viewer/src/main.ts b/frontend/viewer/src/main.ts index c127e290e7..92ebdd6d2c 100644 --- a/frontend/viewer/src/main.ts +++ b/frontend/viewer/src/main.ts @@ -22,7 +22,7 @@ if (!window.lexbox.IsDotnetHosted) { useEventBus(); // Wire up globally-accessible helpers for hosts (e.g., MAUI) -window.lexbox.SvelteNavigate = (url: string, options?: { replace?: boolean }) => navigate(url, options); +window.lexbox.SvelteNavigate = (url: string, options?: { replace?: boolean }) => navigate(url, options); //don't mount the app until after we've loaded the local void setLanguage('default') From 868b218fafe7c889f574345c72d7a2d0648b8ad2 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 16 Jun 2026 11:05:31 +0700 Subject: [PATCH 5/6] Enhance FwLiteMaui by adding a new shortcut for sharing debug logs, updating the App class to accept an IServiceProvider, and improving app action handling for better integration with troubleshooting services. --- backend/FwLite/FwLiteMaui/App.xaml.cs | 4 +++- backend/FwLite/FwLiteMaui/MauiProgram.cs | 15 +++++++++++++-- backend/FwLite/FwLiteMaui/Shortcuts.cs | 4 +++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/FwLite/FwLiteMaui/App.xaml.cs b/backend/FwLite/FwLiteMaui/App.xaml.cs index 0f400925a3..88511ac633 100644 --- a/backend/FwLite/FwLiteMaui/App.xaml.cs +++ b/backend/FwLite/FwLiteMaui/App.xaml.cs @@ -3,11 +3,13 @@ namespace FwLiteMaui; public partial class App : Application { + public IServiceProvider ServiceProvider { get; } private readonly MainPage _mainPage; public static string? OverrideStartupUrl { get; set; } - public App(MainPage mainPage) + public App(MainPage mainPage, IServiceProvider serviceProvider) { + ServiceProvider = serviceProvider; _mainPage = mainPage; InitializeComponent(); } diff --git a/backend/FwLite/FwLiteMaui/MauiProgram.cs b/backend/FwLite/FwLiteMaui/MauiProgram.cs index 66a82b0db3..1497b74ab1 100644 --- a/backend/FwLite/FwLiteMaui/MauiProgram.cs +++ b/backend/FwLite/FwLiteMaui/MauiProgram.cs @@ -1,3 +1,4 @@ +using FwLiteShared.Services; using Microsoft.Extensions.Logging; using Microsoft.Maui.LifecycleEvents; @@ -61,11 +62,21 @@ public static MauiApp CreateMauiApp() essentialsBuilder.OnAppAction(action => { + var app = (App?)Application.Current; + if (app is null) return; if (Shortcuts.TryGetUrl(action.Id, out var url)) { - Application.Current!.Dispatcher.Dispatch(() => + app.Dispatcher.Dispatch(() => { - ((App?)Application.Current)?.LoadAppUrl(url); + app.LoadAppUrl(url); + }); + } + else if (action.Id == Shortcuts.ShareLogOut) + { + app.Dispatcher.Dispatch(() => + { + _ = app.ServiceProvider.GetRequiredService() + .ShareLogFile(); }); } }); diff --git a/backend/FwLite/FwLiteMaui/Shortcuts.cs b/backend/FwLite/FwLiteMaui/Shortcuts.cs index 755854aff8..20c34a5c53 100644 --- a/backend/FwLite/FwLiteMaui/Shortcuts.cs +++ b/backend/FwLite/FwLiteMaui/Shortcuts.cs @@ -6,6 +6,7 @@ public static class Shortcuts { // Define shortcut IDs here public const string Home = "home"; + public const string ShareLogOut = "share-log-out"; // Map IDs to URL paths private static readonly IReadOnlyDictionary IdToUrl = new Dictionary @@ -16,7 +17,8 @@ public static class Shortcuts // Titles/subtitles shown in the system UI (if supported) public static readonly IReadOnlyList Declarations = [ - new(Home, "Home") + new(Home, "Home"), + new(ShareLogOut, "Share Debug Log"), ]; public static bool TryGetUrl(string? id, out string url) From 102f66be372946bf6669f30451b363545b9c052d Mon Sep 17 00:00:00 2001 From: Tim Haasdyk Date: Wed, 17 Jun 2026 12:34:41 +0200 Subject: [PATCH 6/6] Minor touch ups --- backend/FwLite/FwLiteMaui/MainPage.xaml.cs | 28 ++++++++++++++-------- backend/FwLite/FwLiteMaui/MauiProgram.cs | 16 +++++++++---- backend/FwLite/FwLiteMaui/Shortcuts.cs | 4 ---- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs index 11af842636..a20532ff48 100644 --- a/backend/FwLite/FwLiteMaui/MainPage.xaml.cs +++ b/backend/FwLite/FwLiteMaui/MainPage.xaml.cs @@ -1,14 +1,17 @@ -using System.Text.Json; using FwLiteShared.Services; using Microsoft.AspNetCore.Components.WebView; +using Microsoft.Extensions.Logging; using Microsoft.JSInterop; namespace FwLiteMaui; public partial class MainPage : ContentPage { - public MainPage(IPreferencesService preferences) + private readonly ILogger _logger; + + public MainPage(IPreferencesService preferences, ILogger logger) { + _logger = logger; InitializeComponent(); var lastUrlFromPrefs = preferences.Get(nameof(PreferenceKey.AppLastUrl)); blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing; @@ -38,17 +41,22 @@ internal void LoadAppUrl(string url) { var jsRuntime = services.GetService(); if (jsRuntime != null) - { - var js = $$""" - if (window.lexbox.SvelteNavigate) { - window.lexbox.SvelteNavigate({{JsonSerializer.Serialize(url)}}, { replace: true }); - } - """; - _ = jsRuntime.InvokeVoidAsync("eval", js); - } + _ = NavigateAsync(jsRuntime, url); }); } + private async Task NavigateAsync(IJSRuntime jsRuntime, string url) + { + try + { + await jsRuntime.InvokeVoidAsync("lexbox.SvelteNavigate", url, new { replace = true }); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to navigate to {Url} via SvelteNavigate", url); + } + } + #if ANDROID || WINDOWS private partial void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e); private partial void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e); diff --git a/backend/FwLite/FwLiteMaui/MauiProgram.cs b/backend/FwLite/FwLiteMaui/MauiProgram.cs index 1497b74ab1..7a66e0b314 100644 --- a/backend/FwLite/FwLiteMaui/MauiProgram.cs +++ b/backend/FwLite/FwLiteMaui/MauiProgram.cs @@ -69,14 +69,22 @@ public static MauiApp CreateMauiApp() app.Dispatcher.Dispatch(() => { app.LoadAppUrl(url); - }); + }); } else if (action.Id == Shortcuts.ShareLogOut) { - app.Dispatcher.Dispatch(() => + _ = app.Dispatcher.DispatchAsync(async () => { - _ = app.ServiceProvider.GetRequiredService() - .ShareLogFile(); + try + { + await app.ServiceProvider.GetRequiredService() + .ShareLogFile(); + } + catch (Exception e) + { + app.ServiceProvider.GetService>()? + .LogError(e, "Failed to share log file from app action"); + } }); } }); diff --git a/backend/FwLite/FwLiteMaui/Shortcuts.cs b/backend/FwLite/FwLiteMaui/Shortcuts.cs index 20c34a5c53..591112bf5a 100644 --- a/backend/FwLite/FwLiteMaui/Shortcuts.cs +++ b/backend/FwLite/FwLiteMaui/Shortcuts.cs @@ -1,14 +1,10 @@ -using System.Collections.ObjectModel; - namespace FwLiteMaui; public static class Shortcuts { - // Define shortcut IDs here public const string Home = "home"; public const string ShareLogOut = "share-log-out"; - // Map IDs to URL paths private static readonly IReadOnlyDictionary IdToUrl = new Dictionary { [Home] = "/",