Onboarding guide
25. ADC Application Host, UI Shell & Cross-Module Composition
What this chapter covers. Every ADC module described so far, Conference, Engagement, Identity,
Notification, is consumed somewhere. This chapter is that somewhere: the client tier of ADC,
the code that turns the shared per-module Razor Class Libraries (MMCA.ADC.{Module}.UI) and the
framework UI package (MMCA.Common.UI) into actually running
applications, and composes every conference module into one coherent shell. Two application shapes
are built from the same component set: a Blazor Web app (Server prerender plus a WebAssembly
client) and a .NET MAUI Blazor Hybrid native app for Android, iOS, macOS (Catalyst), and
Windows. The types this group owns are deliberately thin: the two
ADCHomePageContent home-content adapters, the MAUI-only services
(MauiTokenStorageService,
MauiPublicLinkBuilder,
AppActionsInitializer), the MAUI-head composition and native entry
surfaces (DeviceUIModule,
WebAuthenticatorCallbackActivity,
NowNextWidgetProvider with its local
NowNextSnapshot/NowNextSession records), and the MAUI
bootstrap chain (MauiProgram, App, MainPage, and the
per-OS entry points MainActivity, MainApplication,
AppDelegate, the iOS Program, and the WinUI App). The
heavy lifting lives below them in the modules and in
MMCA.Common.UI; this chapter is about wiring and hosting,
the half of "UI architecture" the modules cannot do for themselves. File paths in this chapter are
given as the unit table gives them, relative to MMCA.ADC/Source/Hosts/UI/.
Three hosts, one shared component set. The central idea, taught in
primer §2, "Write-once UI, render everywhere",
is that a page is authored once as a Razor component in a module's UI library and then rendered
by every host without per-platform reimplementation. There are three host projects under
MMCA.ADC/Source/Hosts/UI/: MMCA.ADC.UI.Web (the Blazor Server host, SSR prerender plus the
interactive Server circuit), MMCA.ADC.UI.Web.Client (the Blazor WebAssembly client, compiled
to run in the browser), and MMCA.ADC.UI (the .NET MAUI host, which packages the same
components into a native app and renders them in a BlazorWebView). Read the three composition
roots side by side (MMCA.ADC.UI.Web/Program.cs:33-90, MMCA.ADC.UI.Web.Client/Program.cs:31-68,
MMCA.ADC.UI/MauiProgram.cs:59-104) and the family resemblance is obvious: the same MudBlazor
registration, the same AddUIShared(builder.Configuration), the same four conditional module
registrations, then a short tail of host-specific adapters. [Rubric §18, UI Architecture] assesses
cohesive, composable components and a clean host/shell split; [Rubric §22, Responsive & Cross-Browser/Device] assesses that one UI renders correctly across browsers and devices. Both are
embodied by this single-component-set, multi-host design: adding a platform is "add a host that
references the shared libraries," not "fork the UI."
The shared-vs-host boundary: interfaces in Common, implementations per host. The reason one
component can run in three places is that everything platform-specific hides behind interfaces
declared in MMCA.Common.UI. A component never asks "am I on
MAUI?"; it asks an injected abstraction, and each host supplies the adapter. [Rubric §1, SOLID]
(Dependency Inversion) and [Rubric §3, Clean Architecture] are visible right here: the shared
library defines the ports, the host defines the adapters, and the framework depends on nothing
host-specific. Home-page content:
IHomePageContent lets the shared / route
render an app-specific landing page, and each head registers its own
ADCHomePageContent (web MMCA.ADC.UI.Web/Program.cs:50 and
MMCA.ADC.UI.Web.Client/Program.cs:43, MAUI MMCA.ADC.UI/MauiProgram.cs:73). Token storage:
ITokenStorageService abstracts where JWTs
live; this group supplies the MAUI implementation
MauiTokenStorageService
(MMCA.ADC.UI/MauiProgram.cs:98), while the browser heads get
ServerTokenStorageService
(via AddCommonServerTokenStorage(), MMCA.ADC.UI.Web/Program.cs:63) and
WasmTokenStorageService
(MMCA.ADC.UI.Web.Client/Program.cs:46). Form factor is the same story with three registration
lines: AddCommonWebFormFactor(), AddWasmFormFactor(), and AddMauiFormFactor()
(MMCA.ADC.UI.Web/Program.cs:83, MMCA.ADC.UI.Web.Client/Program.cs:68,
MMCA.ADC.UI/MauiProgram.cs:104), all satisfying the same
IFormFactor contract. OAuth button availability
(IOAuthUISettings, satisfied by
ConfigurationOAuthUISettings in
all three heads) and the device-capability layer
(IDeepLinkDispatcher and friends,
AddBrowserDeviceCapabilities() on the web heads versus UseMauiDeviceCapabilities() on MAUI) work
the same way and are described in Group 15 and the
Group 26 device-capability layer.
Registration order is load-bearing. A detail worth internalizing before reading any host's
composition root: AddUIShared installs its defaults with TryAdd, so an override must be
registered either before it (to pre-empt the TryAdd) or after the module registrations (so
"last registration wins"). Both directions appear here and both are annotated in source.
IOAuthUISettings is registered before
AddUIShared on every head so the social-login buttons appear
(MMCA.ADC.UI/MauiProgram.cs:62-66); UseMauiDeviceCapabilities() and
AddBrowserDeviceCapabilities() run after it so their plain Add registrations beat the null
defaults (MMCA.ADC.UI/MauiProgram.cs:68-71, MMCA.ADC.UI.Web/Program.cs:45-48); and
MauiPublicLinkBuilder is registered after AddConferenceUI() so it
displaces Conference's browser-origin default of
IPublicLinkBuilder
(MMCA.ADC.UI/MauiProgram.cs:88-90). Similarly, the Blazor host registers its dynamic
Content-Security-Policy provider (AddCommonBlazorCsp(), backed by
BlazorCspPolicyProvider) before
AddCommonSecurityHeaders(...) so it wins over the static default
(MMCA.ADC.UI.Web/Program.cs:89-90), feeding the framework's
SecurityHeadersMiddleware over the
ICspPolicyProvider boundary.
Which modules are in the build is configuration, not code. All three heads gate every module UI
behind UIModuleConfiguration.IsModuleEnabled
(MMCA.ADC.UI/MauiProgram.cs:76-86 and the matching blocks in the two web hosts), so a deployment
can ship Conference-only, or Conference plus Engagement, without touching source. That is the
client-side mirror of the server-side module system in
Group 14: each enabled module contributes its
IUIModule descriptor, and the shell composes nav
items, routable assemblies, and layout components from whatever is registered. On the web host the
composition is explicit at the end of Program.cs: every registered IUIModule's Assembly is
concatenated with the shared UI assemblies, deduplicated, and handed to
MapRazorComponents<App>().AddAdditionalAssemblies(...)
(MMCA.ADC.UI.Web/Program.cs:172-186). This is the group's cleanest
[Rubric §16, Maintainability] and [Rubric §25, Navigation, Routing & IA] moment: routes and
navigation are discovered from the enabled module set rather than maintained in a central list.
The landing page and the two content adapters. The conference landing page itself is not
owned by this group any more: ADCHome lives once in Conference's UI library and is documented in
Group 21. What this group owns are the two thin adapters that
point the shared / route at it. The web adapter returns the shared component directly, aliased at
the using site (MMCA.ADC.UI.Web.Client/Pages/ADCHomePageContent.cs:2,13), because the Web head's
site-root asset paths already match the component's defaults. The MAUI adapter returns a local
ADCHome.razor wrapper (MMCA.ADC.UI/Pages/ADCHomePageContent.cs:10) whose entire body is one
element: the shared Conference component with ImageBasePath="_content/MMCA.Common.UI/images"
(MMCA.ADC.UI/Pages/ADCHome.razor:5), because MAUI serves those images out of the Razor Class
Library content root instead of the site root. Both adapters return the same page title,
"Atlanta Developers Conference," carrying an explicit // i18n: allow comment marking the brand
name as a deliberate localization exemption (ADR-027). That one-parameter difference is the whole
per-head divergence of the landing page, and it is a good measure of how far the write-once story
actually goes.
The MAUI bootstrap chain (ADR-042). Every platform entry point does nothing but call
MauiProgram.CreateMauiApp(): MainApplication on Android
(MMCA.ADC.UI/Platforms/Android/MainApplication.cs:17), AppDelegate plus the iOS
Program on iOS (MMCA.ADC.UI/Platforms/iOS/AppDelegate.cs:19,
MMCA.ADC.UI/Platforms/iOS/Program.cs:10-11), and the WinUI App on Windows
(MMCA.ADC.UI/Platforms/Windows/App.xaml.cs:16), while
MainActivity is the Android launcher activity
(MMCA.ADC.UI/Platforms/Android/MainActivity.cs:27). CreateMauiApp
(MMCA.ADC.UI/MauiProgram.cs:34) builds the entire DI and configuration graph. Because MAUI does
not auto-load appsettings.json from disk, it reads it from an embedded resource
(MauiProgram.cs:48-57), then registers the BlazorWebView and MudBlazor, the CommunityToolkit
(required by the ADR-042 speech-to-text capability, MauiProgram.cs:41), the shared UI
(MauiProgram.cs:66), the native device capabilities (MauiProgram.cs:71), the home content
(MauiProgram.cs:73), the module UIs (MauiProgram.cs:76-86), and the MAUI flavors of the token,
refresh, and auth-state services (MauiProgram.cs:98-101). The MAUI head also registers its own
composition pieces: DeviceUIModule as an
IUIModule contributing the Device settings nav item
plus four shared layout components (MauiProgram.cs:94,
MMCA.ADC.UI/DeviceUIModule.cs:22-30), and AppActionsInitializer as an
IMauiInitializeService that sets localized home-screen quick actions after build
(MauiProgram.cs:95). The cross-platform App (MMCA.ADC.UI/App.xaml.cs:11) creates the
single window hosting MainPage, and MainPage (MMCA.ADC.UI/MainPage.xaml.cs:13)
hosts the BlazorWebView and intercepts the hardware/gesture back button
(MainPage.xaml.cs:18-23), forwarding it to the WebView's own history via
MauiBackNavigationBridge
(MainPage.xaml.cs:48) and quitting only when there is nowhere left to go back to
(MainPage.xaml.cs:49-52). [Rubric §25, Navigation, Routing & IA] shows up in that bridge: native
back must map onto in-app navigation, not OS app-switching, or the native experience feels broken.
MainActivity's ConfigurationChanges attribute (MainActivity.cs:16-20) is the other native
subtlety: it stops Android from destroying the activity (and with it the Blazor render tree and all
component state) on rotation or dark-mode toggle.
Native entry surfaces all funnel into one dispatcher (ADR-042 / ADR-043). Several MAUI-head
types exist for one purpose: to bring the platform's native entry points back into the same in-app
navigation the WebView already runs. MainActivity declares an IntentFilter for
verified Android App Links (https URLs on the pinned public web host,
MainActivity.cs:21-31) and publishes their path plus query to
IDeepLinkDispatcher from both OnCreate
and OnNewIntent (MainActivity.cs:34-62); AppDelegate does the equivalent for
iOS Universal Links in ContinueUserActivity
(MMCA.ADC.UI/Platforms/iOS/AppDelegate.cs:22-40); and
WebAuthenticatorCallbackActivity receives the custom-scheme
OAuth completion redirect (atldevcon://,
MMCA.ADC.UI/Platforms/Android/WebAuthenticatorCallbackActivity.cs:14-21) so MAUI's
WebAuthenticator can resume the pending social-login flow.
AppActionsInitializer maps three home-screen quick actions to routes
(RouteFor, MMCA.ADC.UI/Services/AppActionsInitializer.cs:39-45) that MauiProgram.HandleAppAction
publishes into the same dispatcher (MauiProgram.cs:131-144). The Android home-screen
NowNextWidgetProvider is a self-contained, best-effort surface: on each
update it fetches the anonymous Events/now-next snapshot into its local
NowNextSnapshot and NowNextSession records
(MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:109-134), renders one "Now" and one "Next"
line, and taps back into MainActivity's deep-link path
(NowNextWidgetProvider.cs:85-96); it never throws, keeping the last rendered content on any failure
(NowNextWidgetProvider.cs:57-62). Sharing and copying links is the mirror-image problem, and
MauiPublicLinkBuilder solves it by resolving against the pinned
PublicSite:BaseUrl from the embedded configuration
(MMCA.ADC.UI/Services/MauiPublicLinkBuilder.cs:18-32), so a link copied inside the app opens the
public web app rather than a WebView-internal origin. The web side of that association is served by
the Blazor host, which maps the App Links and Universal Links association documents from
configuration (MMCA.ADC.UI.Web/Program.cs:158-165), and the applink components mirror the same
Blazor routes the app uses: identical URLs on web and device, no route translation table.
Host security: platform-appropriate token handling. The token-storage implementations are a
compact study in secret handling matched to the threat model. On the browser heads the
high-value refresh token is never exposed to JavaScript: it stays in an HttpOnly cookie and is
exchanged through a same-origin proxy refresher (SameOriginProxyTokenRefresher,
MMCA.ADC.UI.Web/Program.cs:64, MMCA.ADC.UI.Web.Client/Program.cs:47), and the Server head
additionally runs a cookie-backed SSR authentication scheme so [Authorize] component routes survive
F5 and open-in-new-tab (MMCA.ADC.UI.Web/Program.cs:56-62,128). On MAUI, which has no DOM and
therefore no XSS surface, MauiTokenStorageService stores both tokens in
OS SecureStorage, the platform secure enclave (Android Keystore, iOS Keychain, Windows DPAPI),
under two fixed key names (MMCA.ADC.UI/Services/MauiTokenStorageService.cs:11-12,16-29).
[Rubric §11, Security] (at-rest secret handling) and [Rubric §26, Front-End Security] (no token
reachable from page JS) are both directly embodied; the deeper design note is
MMCA.ADC/TokenStorageDesignNote.md. One deliberate development-only relaxation lives in
MauiProgram: a #if DEBUG block installs a SocketsHttpHandler that bypasses SSL certificate
validation (MauiProgram.cs:106-126) so a MAUI device on the LAN can reach the API over the ASP.NET
dev cert; it is scoped to DEBUG, analyzer-suppressed inline with a justification, and not a
production path.
Localization of the shell (ADR-027). All three heads share one localization stance and each
implements its own half of it. The Blazor Server host sets CurrentUICulture from the culture cookie
before SSR prerender and exposes a culture-switch endpoint
(MMCA.ADC.UI.Web/Program.cs:114,150); the WASM client mirrors the same cookie into the browser
thread culture before the app runs, so there is no locale flash or prerender/hydration mismatch
(MMCA.ADC.UI.Web.Client/Program.cs:74). On MAUI the same convention flows into composition:
DeviceUIModule declares its nav item with a resource key and a
TitleResource type rather than a literal (MMCA.ADC.UI/DeviceUIModule.cs:22-25), and
AppActionsInitializer resolves quick-action titles through an
IStringLocalizer before handing them to the OS
(MMCA.ADC.UI/Services/AppActionsInitializer.cs:31,51-57). [Rubric §27, Internationalization & Localization] assesses externalized strings and culture-aware formatting; the rule this codebase
follows is "localize the chrome, exempt the branded and editorial data on purpose, and mark the
exemption in source," which is exactly what the two ADCHomePageContent adapters do with the
conference brand name.
How it all fits at runtime. A request to the Blazor Web host renders the shared layout from
MMCA.Common.UI; the navbar is composed from each enabled
module's IUIModule descriptor, and / renders the Conference landing page through
ADCHomePageContent. After prerender, the interactive Server circuit or the
downloaded WASM runtime takes over; auth state flows through
JwtAuthenticationStateProvider
reading whichever ITokenStorageService the
host registered, and the WASM client discovers its API endpoint at startup from the Server host's
/client-config endpoint instead of having it baked into the static bundle
(MMCA.ADC.UI.Web/Program.cs:135-145, MMCA.ADC.UI.Web.Client/Program.cs:27-29). On MAUI the same
component tree runs inside a BlazorWebView with SecureStorage-backed tokens, the native
back-button bridge, App Link and Universal Link entry, OAuth callback resumption, quick actions, and
the home-screen widget, all funneled into one deep-link dispatcher. In every case the application
talks to the backend only through the YARP Gateway: the same boundary that makes the modules
independently extractable (ADR-007 gRPC extraction, ADR-008 service-extraction topology) also makes
the UI host-agnostic. The client points at one origin, and the Gateway routes to whichever service
owns the endpoint. That is the unifying theme of this chapter: thin hosts over shared components,
talking to one gateway, with every platform difference pushed behind a Common interface.
NowNextSession
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:134· Level 0 · record (sealed, private)
- What it is: a tiny wire-shape record for one session row rendered by the Android home-screen widget:
Title, an optionalRoomName, and aStartsAtLocaltimestamp. It is a private nested type of NowNextWidgetProvider. - Depends on: only the BCL (
string,DateTime). No first-party types. - Concept introduced, local mirror of a server DTO. The widget deliberately does not reference the
Conference.Sharedassembly just to deserialize one payload; instead it declares its own record whose property names match the JSON the server sends (the inline comment atMMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:130-131says exactly this).System.Text.Jsonpopulates it by name from theEvents/now-nextresponse. - Walkthrough: the positional record (
NowNextWidgetProvider.cs:134) is consumed only byFormatRow(NowNextWidgetProvider.cs:101-107), which formatsStartsAtLocalasHH:mmunderCultureInfo.InvariantCulture(:103), appends the room in parentheses when it is non-blank (:104), and adds a+Nsuffix when more than one session shares the slot (:105). - Why it's built this way: keeping the widget's dependency surface to the BCL plus the Android SDK avoids pulling a module-shared contract assembly into a
BroadcastReceiverthat runs in a minimal process. The property-name coupling to the server DTO is the trade-off, documented inline. - Where it's used: the
NowandNextlists on NowNextSnapshot; read byNowNextWidgetProvider.BuildViewsandFormatRow.
WebAuthenticatorCallbackActivity
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/WebAuthenticatorCallbackActivity.cs:19· Level 0 · class
- What it is: the Android activity that catches the custom-scheme OAuth completion redirect and hands control back to MAUI's
WebAuthenticator. After the Identity service'sCompleteAsyncfinishes a social login it redirects the system browser toatldevcon://oauth-complete?code=...; Android routes that URI here, and the base class resumes the pendingAuthenticateAsyncwith the captured parameters (ADR-043, documented atMMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/WebAuthenticatorCallbackActivity.cs:7-13). - Depends on:
Microsoft.Maui.Authentication.WebAuthenticatorCallbackActivity(base class) and the Android SDK activity/intent attributes. No first-party types. - Concept introduced, custom-scheme OAuth return on mobile. Unlike the web heads (which get an ordinary HTTP redirect), a native app receives the OAuth result through a registered URI scheme. The
[IntentFilter](WebAuthenticatorCallbackActivity.cs:15-18) declares the app as a handler forActionViewintents whoseDataSchemeis theCallbackSchemeconstant, with theDefaultandBrowsablecategories so a browser can launch it.NoHistory = trueandLaunchMode.SingleTop(:14) keep the callback out of the back stack and reuse the existing task. [Rubric §26, Front-End Security] assesses how client auth flows avoid token leakage: the scheme is an allowlisted return target and the class body holds nothing but the constant, so there is no place for a token to be logged or mishandled here. - Walkthrough: the class is behavior-free by design (
WebAuthenticatorCallbackActivity.cs:19-22): the whole contract lives in the attributes, andCallbackScheme = "atldevcon"(:21) must stay in lockstep withOAuth:MobileRedirectSchemein the embeddedappsettings.jsonand the Identity service'sOAuth:AllowedReturnUrlSchemesallowlist (class summary,:11-12). - Why it's built this way: subclassing the MAUI base activity is the framework-sanctioned way to receive the redirect; all the app supplies is the scheme and the intent-filter metadata. Keeping the scheme constant next to the filter makes the three-place coupling (app, config, Identity allowlist) easy to keep aligned during a cutover.
- Where it's used: invoked by the Android OS during the OAuth flow enabled by the IOAuthUISettings registration in MauiProgram (
MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MauiProgram.cs:64); it is never called from managed code.
ADCHomePageContent
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHomePageContent.cs:11· Level 1 · class (sealed)
- What it is: the ADC web heads' binding of the framework's IHomePageContent extension point. It tells the shared
Home.razorshell which component to render as the landing page and what title to show. - Depends on: IHomePageContent (implements,
MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHomePageContent.cs:1,11) and ADCHome fromMMCA.ADC.Conference.UI, imported through theSharedADCHomeusing-alias (:2). - Concept introduced, app-supplied content for a shared shell. The framework ships one generic home shell; each host app registers a single
IHomePageContentthat hands the shell aComponentTypeand aPageTitle. The dependency is inverted, the shared shell never references an ADC page. [Rubric §18, UI Architecture] assesses how a reusable shell is specialized per app: this is the whole specialization, two properties. [Rubric §2, Design Patterns] applies as well, since this is a minimal strategy/adapter at a UI boundary. - Walkthrough: two expression-bodied properties and no state.
ComponentType => typeof(SharedADCHome)(ADCHomePageContent.cs:13) points the shell at the Conference module's shared landing component rather than a page owned by this project;PageTitle => "Atlanta Developers Conference"(:15) carries an expliciti18n: allowmarker because the conference brand name is deliberately not localized. The class summary notes thatADCHome's default image base path already matches the web head's site-root assets (:6-9), which is why no parameters are passed. - Why it's built this way: pointing at the Conference module's component instead of duplicating a landing page means the web and MAUI heads render the same marketing surface, and a change to the conference home lands everywhere at once.
- Where it's used: registered as a singleton
IHomePageContentby both web heads, the WebAssembly client (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Program.cs:43) and the Blazor Server host (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web/Program.cs:50, which importsMMCA.ADC.UI.Web.Client.Pagesat:11). The MAUI head registers a separate same-named class fromMMCA.ADC.UI.Pagesinstead (MauiProgram.cs:73).
AppActionsInitializer
MMCA.ADC.UI ·
MMCA.ADC.UI.Services·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/AppActionsInitializer.cs:15· Level 1 · class (sealed)
- What it is: a MAUI startup service that publishes the three home-screen quick actions (the long-press app-icon shortcuts) once the app is built, with localized titles, and that owns the lookup mapping an action id back to an in-app route.
- Depends on:
IMauiInitializeService(the MAUI hosting contract it implements),IStringLocalizer<AppActionsInitializer>, MAUI Essentials'AppActions/AppAction/FeatureNotSupportedException; route constants from EngagementRoutePaths and NotificationRoutePaths; its published routes travel through IDeepLinkDispatcher. - Concept introduced, native quick actions as a navigation entry point. [Rubric §25, Navigation & IA] assesses whether the app exposes coherent first-class entry points: the three shortcut ids (
happening_now,my_schedule,notifications,MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/AppActionsInitializer.cs:17-19) are OS-level jump points into deep routes, not in-app links. [Rubric §27, i18n] applies because the shortcut labels are resolved from the co-located resx pair through the injected localizer at registration time (:53-55), so they follow the selected language rather than shipping as English literals. - Walkthrough
Initialize(IServiceProvider services)(AppActionsInitializer.cs:22-36): null-guards the provider (:24), returns immediately whenAppActions.Current.IsSupportedis false (:26-29), resolves the localizer (:31), then startsSetActionsAsyncfire-and-forget with a discard (:35) so a slow or failing shortcut registration can never block or fail app startup (the inline comment states this at:33-34).RouteFor(string actionId)(AppActionsInitializer.cs:39-45): aninternal staticswitch expression mapping each id to its app-relative route,EngagementRoutePaths.HappeningNow, the literal/conference/sessions?mine=true, andNotificationRoutePaths.NotificationInbox, returningnullfor anything unknown. This is the lookup the activation handler in MauiProgram calls.SetActionsAsync(IStringLocalizer<AppActionsInitializer>)(AppActionsInitializer.cs:47-64): builds the threeAppActions with localized titles and theappiconicon (:51-56), awaitsAppActions.Current.SetAsync, and catchesFeatureNotSupportedException(:59-63) because some launchers report support and then reject the call at runtime, in which case the shortcuts simply do not appear.
- Why it's built this way: registration and activation are deliberately split. This initializer sets the shortcuts and their titles, while MauiProgram wires
ConfigureEssentials(... OnAppAction(HandleAppAction))toRouteFor; both ends publish the resolved route into the ADR-042 deep-link dispatcher, which buffers a cold-start activation until the sharedDeepLinkListenerrenders (class summary,AppActionsInitializer.cs:8-14). - Where it's used: registered as a singleton
IMauiInitializeServicein MauiProgram (MauiProgram.cs:95) so MAUI runsInitializeduring startup;RouteForis called fromMauiProgram.HandleAppAction(MauiProgram.cs:133). - Caveats / not-in-source: whether a given launcher actually surfaces the shortcuts is a runtime platform behavior, not determinable from source; the code only handles the rejection case.
MauiPublicLinkBuilder
MMCA.ADC.UI ·
MMCA.ADC.UI.Services·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/MauiPublicLinkBuilder.cs:13· Level 1 · class (sealed)
- What it is: the MAUI implementation of IPublicLinkBuilder. It turns a relative path into an absolute URL rooted at the public web app, so a link the user shares or copies from the device points at the public site rather than the WebView's internal origin.
- Depends on: IPublicLinkBuilder (implements),
Microsoft.Extensions.Configuration.IConfiguration,System.Uri. - Concept introduced, per-head override of a shared UI service. On a browser head the default builder can resolve against the current origin, but a MAUI WebView's origin is an internal shell address that is meaningless once pasted into a message. This head therefore substitutes a base URL pinned in configuration. [Rubric §25, Navigation & IA] assesses whether links resolve to real destinations, and [Rubric §26, Front-End Security] is touched because the shared link is bound to one configured host instead of whatever origin the WebView happens to report. The override only works because it is registered after the module registrations in MauiProgram (last plain
Addwins, class summary atMMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/MauiPublicLinkBuilder.cs:6-12). - Walkthrough
- The constructor (
MauiPublicLinkBuilder.cs:18-24) readsPublicSite:BaseUrlfrom the embedded configuration and throwsInvalidOperationExceptionwhen it is missing or blank (:21-23), a fail-fast that stops a misconfigured build from silently emitting broken share links. The parsed value is stored in the readonly_baseUrlfield (:15) as an absoluteUri. BuildAbsolute(string relativePath)(MauiPublicLinkBuilder.cs:27-32) guards against a null or whitespace path withArgumentException.ThrowIfNullOrWhiteSpace(:29), then combines the path onto the base via theUri(baseUri, relative)constructor (:31).
- The constructor (
- Why it's built this way: the same
PublicSite:BaseUrlvalue also backs the Android App Link host constant in MainActivity, so one configuration key defines "the public site" for both outbound share links and inbound deep links. - Where it's used: registered as scoped in MauiProgram (
MauiProgram.cs:90), deliberately afterAddConferenceUI(); consumed by the Conference UI's share and copy-link surfaces in Group 21.
MauiTokenStorageService
MMCA.ADC.UI ·
MMCA.ADC.UI.Services·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/MauiTokenStorageService.cs:9· Level 1 · class (sealed)
- What it is: the MAUI implementation of ITokenStorageService. It persists the JWT access token and refresh token in the platform secure store through MAUI's
SecureStorage. - Depends on: ITokenStorageService (implements), MAUI Essentials'
SecureStorage. - Concept introduced, at-rest token protection on the device head. [Rubric §11, Security] and [Rubric §26, Front-End Security] assess where credentials live at rest and who can read them. The browser heads are limited to what the browser offers, but the device head can hand the tokens to the OS:
SecureStorage.Defaultroutes to the platform-specific protected store (Android Keystore, iOS Keychain, Windows DPAPI, per the class summary atMMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/MauiTokenStorageService.cs:5-8), so the tokens are encrypted at rest by the platform rather than by app code. - Walkthrough: two fixed key constants,
auth_access_tokenandauth_refresh_token(MauiTokenStorageService.cs:11-12).GetAccessTokenAsync(:14-18) andGetRefreshTokenAsync(:20-24) each read one key viaSecureStorage.Default.GetAsync, returningnullwhen absent.SetTokensAsync(:26-30) writes both keys in sequence.ClearTokensAsync(:32-37) calls the synchronousRemovefor both keys and returnsTask.CompletedTask, so the method is async in signature only. - Why it's built this way: keeping one
ITokenStorageServicecontract across every head means the shared auth pipeline (refresh, state provider, HTTP handler) is head-agnostic and only the storage backend swaps. ADC's privateTokenStorageDesignNote.mdrecords the cross-head storage rationale. - Where it's used: registered as scoped in MauiProgram (
MauiProgram.cs:98), alongside DirectApiTokenRefresher and JwtAuthenticationStateProvider; consumed by the shared auth services in Group 15.
NowNextSnapshot
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:132· Level 1 · record (sealed, private)
- What it is: the deserialized
Events/now-nextpayload the widget renders: anEventName, anIsLiveflag, and two lists of NowNextSession (NowandNext). Like its sibling it is a private nested record on NowNextWidgetProvider. - Depends on: NowNextSession; BCL
List<T>,string,bool. - Concept introduced: reuses the local mirror of a server DTO idea introduced by NowNextSession (the shared comment at
MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:130-131covers both records). No new pattern. - Walkthrough: produced by
FetchSnapshotAsyncviaJsonSerializer.Deserialize<NowNextSnapshot>(json, JsonSerializerOptions.Web)(NowNextWidgetProvider.cs:127), which applies the web (camelCase) naming policy so the record's PascalCase members bind to the server's JSON.BuildViews(:69-99) readsEventNameinto the header text view (:72) and takes the first entry ofNowandNext, substituting a localized "nothing scheduled" string for an emptyNowlist and an empty string for an emptyNextlist (:78-83). - Why it's built this way: one flat record keeps the deserialize-then-render path allocation-light and independent of the Conference module contracts.
- Where it's used: returned by
NowNextWidgetProvider.FetchSnapshotAsync; consumed byNowNextWidgetProvider.BuildViews. - Caveats / not-in-source:
IsLiveis declared on the record and deserialized, but no code path in this file reads it; whether a future render uses it is not determinable from source.
MainPage
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MainPage.xaml.cs:13· Level 2 · class (partial)
- What it is: the single MAUI
ContentPagethat hosts theBlazorWebViewcontrol declared in the paired XAML. Beyond hosting, its one job is to intercept the platform back gesture (Android hardware back, iOS edge swipe) and forward it to the WebView's own Blazor history, exiting the app only when the WebView has nowhere left to go. - Depends on: MauiBackNavigationBridge (the shared back-navigation helper),
Microsoft.JSInterop.IJSRuntime, and MAUI'sContentPage,BlazorWebView,MainThread, andApplication. - Concept introduced, bridging a native gesture into Blazor navigation. In a Blazor Hybrid host the OS back button is a native event, but the user's mental model of "back" is a route change inside the WebView.
OnBackButtonPressed(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MainPage.xaml.cs:18-23) returnstrueto consume the native gesture and starts the async handling off the UI thread. BecauseBlazorWebViewonly exposes the synchronousAction<IServiceProvider>dispatch overload,HandleBackAsynccaptures the renderer-scopedIJSRuntimethrough aTaskCompletionSource(:32-33,:61-62) and then runs the interop outside the dispatch context. [Rubric §25, Navigation & IA] assesses whether the app presents one coherent navigation model: here a single back affordance drives in-app history instead of dumping the user out. [Rubric §18, UI Architecture] applies too, since the native shell and the web content share one navigation contract. - Walkthrough:
MainPage()(MainPage.xaml.cs:15) only callsInitializeComponent().HandleBackAsync(:25-59) dispatchesCaptureJsRuntimeinto the WebView (:33); if dispatch returns false (:35-39), or the captured runtime is null (:41-46), or MauiBackNavigationBridge.HandleBackPressedAsyncreportsAtRoot(:48-52), it callsQuitApp. A barecatch(:54-58) quits as well, on the reasoning that a not-yet-hydrated WebView or a failed interop should exit cleanly rather than leave the gesture doing nothing.CaptureJsRuntime(:61-62) resolvesIJSRuntimefrom the dispatched provider into the completion source, andQuitApp(:64-68) marshalsApplication.Current?.Quit()back onto the main thread. - Why it's built this way: the
TaskCompletionSourcehop is a direct consequence of the dispatch overload MAUI offers; awaiting interop inside that synchronous dispatch would deadlock. Treating every failure as "quit" keeps the back gesture deterministic. - Where it's used: constructed by App as the content of the single window (
MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/App.xaml.cs:11); it is the visual root on every platform head. - Caveats / not-in-source: the
blazorWebViewfield referenced atMainPage.xaml.cs:33is generated from the pairedMainPage.xaml, which is not part of this file.
App
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/App.xaml.cs:7· Level 3 · class (partial)
- What it is: the cross-platform MAUI
Applicationroot. It creates the single window that hosts MainPage, and therefore the Blazor WebView. - Depends on: MainPage; MAUI's
Application,Window, andIActivationState. - Concept introduced, the MAUI application object. One
Appper process owns the window graph. HereCreateWindow(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/App.xaml.cs:11) returns a singleWindowwrapping a freshMainPage, titled"MMCA.ADC.UI". Contrast this with the per-platform entry points (AppDelegate, MainApplication, Program), which boot the framework and then defer to this shared class. - Walkthrough: two members only. The constructor (
App.xaml.cs:9) callsInitializeComponent()from the XAML-generated partial, andCreateWindow(IActivationState?)(:11) is the sole override. There are no lifecycle hooks and no DI wiring; that all lives in MauiProgram. - Why it's built this way: keeping
Appto a single-window factory concentrates composition inMauiProgramand navigation inMainPage, so the application root stays trivial and platform-agnostic. - Where it's used: named as the app type in
builder.UseMauiApp<App>()(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MauiProgram.cs:38); the MAUI framework instantiates it after each platform head callsCreateMauiApp().
DeviceUIModule
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/DeviceUIModule.cs:18· Level 3 · class (sealed)
- What it is: the MAUI-head-only UI module (ADR-042 Wave 2). It contributes the Device settings page plus its nav item, and it registers the layout components that turn native device events into in-app behavior. Web heads never register it, so none of its pages or components exist there.
- Depends on: IUIModule (implements) and NavItem; the local
AppLockKeyMigrationcomponent plus the sharedDeepLinkListener,BiometricGate, andPushRegistrationListenercomponents fromMMCA.Common.UI.Components.Capabilities;System.Reflectionand MudBlazorIcons. - Concept introduced, UI modules as a composition unit. IUIModule lets each module contribute nav items, layout components, and its own assembly to the shared router. The shared router's
AppAssemblyisMMCA.Common.UI, so a module'sAssembly(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/DeviceUIModule.cs:32) has to be added toAdditionalAssembliesbefore its[Route]pages resolve at all (class summary,:10-17).NavItems(:22-25) exposes oneDevice settingsentry whoseTitleis a resource key ("Nav.DeviceSettings") rather than display text, resolved at render time by the sharedNavMenuagainst the co-locatedDeviceUIModule.resxpair viaTitleResource: typeof(DeviceUIModule)(ADR-027, comment at:20-21). [Rubric §18, UI Architecture] assesses how features compose into a shell: this is the extension point that lets a device-only capability slot into a shared Blazor UI without the web heads knowing it exists. [Rubric §27, i18n] is touched by deferring the nav title to a resource lookup. - Walkthrough: three get-only auto-properties are the entire surface.
NavItems(DeviceUIModule.cs:22) holds the single nav entry pointing at/settings/devicewith theAppSettingsAlticon.LayoutComponentTypes(:30) lists four components the shared layout renders once each, in a deliberate order:AppLockKeyMigrationfirst so the E7 preference-key rename lands beforeBiometricGateperforms its first read ofDevicePreferenceKeys.AppLockEnabled(comment at:27-29), thenDeepLinkListener,BiometricGate, andPushRegistrationListener.Assembly(:32) returns this project's assembly. There is no constructor logic: the module is a declarative manifest. - Why it's built this way: registering device concerns through the same IUIModule contract the business modules use keeps the MAUI head from special-casing composition, and the ordered
LayoutComponentTypesencodes a real initialization dependency rather than an arbitrary list. - Where it's used: registered as a singleton
IUIModulein MauiProgram (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MauiProgram.cs:94); its components render inside the sharedMMCA.Common.UIlayout.
MainActivity
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/MainActivity.cs:27· Level 3 · class
- What it is: the Android launcher activity for the MAUI host. It does two jobs: declare which configuration changes it handles in-process (so Android does not restart the activity and tear down the Blazor circuit), and receive verified https App Links (ADR-043), publishing their route to the shared deep-link dispatcher for in-app navigation.
- Depends on: IDeepLinkDispatcher (resolved from
IPlatformApplication.Current.Services), MAUI'sMauiAppCompatActivity, and the Android intent/activity SDK. - Concept introduced,
ConfigurationChangesand Blazor circuit preservation. By default Android destroys and recreates an activity on orientation, theme, or density changes; for aBlazorWebViewthat destruction tears down the whole Blazor circuit and loses component state. The[Activity(... ConfigurationChanges = ScreenSize | Orientation | UiMode | ScreenLayout | SmallestScreenSize | Density)]attribute (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/MainActivity.cs:16-20) tells Android the activity handles those events itself, so no recreation happens. The second concept is verified App Links: the[IntentFilter](:21-26) claimshttpsURLs onPublicWebHostwithAutoVerify = true, which only works if a liveassetlinks.jsoncarrying the Play App Signing fingerprint is served from that host (class summary,:12-14). [Rubric §25, Navigation & IA] applies because deep links land the user on the right in-app route, and [Rubric §22, Responsive/Cross-Browser] applies because the config-change handling is what keeps the single WebView UI stable across rotations and theme switches. - Walkthrough:
PublicWebHost(MainActivity.cs:31) is a compile-time constant that must matchPublicSite:BaseUrlin the embeddedappsettings.json, so a custom-domain cutover touches only these two places (:29-30).OnCreate(:34-38) andOnNewIntent(:41-45) both callPublishDeepLink, covering cold start and warm re-entry with identical behavior.PublishDeepLink(:47-62) ignores anything that is not anActionViewintent carrying data (:49-52), ignores a blank path (:54-58), reassemblespathplus optional?query(:60), and publishes the route through IDeepLinkDispatcher (:61), which buffers one route across a cold start until the sharedDeepLinkListenerdrains it. - Why it's built this way: the config-changes list is not boilerplate, dropping any entry silently reintroduces an activity restart that only shows up on a physical device rotation or theme switch. Routing both intent callbacks through one helper keeps cold-start and warm-start deep links behaviorally identical.
- Where it's used: the Android launcher (
MainLauncher = true,MainActivity.cs:18); it is also the explicit target of the widget's tapPendingIntentin NowNextWidgetProvider (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:91).
NowNextWidgetProvider
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:22· Level 4 · class (sealed)
- What it is: the Android home-screen
AppWidgetProvider(ADR-042 Wave 8) that renders a "Now / Next" card. On each update it fetches the anonymous, 60s-cachedGET Events/now-nextsnapshot (the id-less form, where the server picks the live-or-next published event) and renders one "Now" and one "Next" line. It never throws: a failed fetch leaves the previousRemoteViewsin place (class summary,MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/NowNextWidgetProvider.cs:11-18). - Depends on: NowNextSnapshot and NowNextSession (its private records), MainActivity (the tap target),
IConfigurationresolved fromIPlatformApplication.Current.Services,System.Net.Http.HttpClient,System.Text.Json, and the Android widget SDK. - Concept introduced, best-effort background rendering under a platform time budget.
OnUpdate(NowNextWidgetProvider.cs:24-35) must return fast, so after null-guarding its three arguments (:26-29) it callsGoAsync()(:33) to keep the broadcast alive while the snapshot downloads, then startsUpdateWidgetsAsyncwithout awaiting (:34); the comment at:31-32records that the platform budget is roughly 10s, far above one cached GET.UpdateWidgetsAsync(:37-67) wraps the whole flow in atry/catchthat swallows every exception, with theCA1031suppression justified inline (:57-59, a widget update is best-effort and the last rendering stays), and always callspendingResult?.Finish()infinally(:63-66). [Rubric §29, Resilience & Business Continuity] assesses graceful degradation: a network or parse failure degrades to the stale card rather than a visible error. [Rubric §23, Front-End Performance] is engaged by leaning on the server's cache and a short client timeout instead of any local polling loop. - Walkthrough:
BuildViews(NowNextWidgetProvider.cs:69-99) inflates thenownext_widgetlayout, sets the event-name text (:72), reads three localized strings (:74-76), and fills the Now/Next lines throughFormatRow, showing the "nothing scheduled" string whenNowis empty and an empty string whenNextis empty (:78-83). It then builds an explicit tap intent targeting MainActivity withActionViewand the app-internalhttps://app.internal/happening-nowURI (:88-96); theS1075suppression is justified because this is an app-internal route rather than an external address, and only the URI path is consumed by the deep-link publisher (:85-87). ThePendingIntentis createdUpdateCurrent | Immutable(:94-95).FormatRow(:101-107) does the invariantHH:mmformatting described under NowNextSession.FetchSnapshotAsync(:109-128) readsApi:ApiEndpointfrom configuration (:113), returnsnullwhen it is missing (:114-117), builds a short-livedHttpClientwith an 8s timeout (:119), GETs the relativeEvents/now-next(:120), returnsnullon a non-success status (:121-124), and otherwise deserializes withJsonSerializerOptions.Web(:127). - Why it's built this way: a widget runs in a minimal broadcast process where an unhandled exception is user-visible as a broken card, so every path is null-guarded and every failure returns quietly to preserve the prior render. The
GoAsync/Finishpairing is the Android-sanctioned way to do async work from a receiver without triggering an ANR. - Where it's used: registered through
[BroadcastReceiver],[IntentFilter], and[MetaData](NowNextWidgetProvider.cs:19-21) and driven by the AndroidAppWidgetManager; its tap routes into MainActivity's deep-link path. - Caveats / not-in-source: the widget layout and string ids (
Resource.Layout.nownext_widget,Resource.Id.*,Resource.String.*) resolve against generated Android resources declared inPlatforms/Android/Resources, not in this file. The comment at:111-112asserts thatMainApplicationhas already initialized MAUI by the time a receiver runs in this process; the code still bails quietly if configuration is unresolvable.
MauiProgram
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MauiProgram.cs:28· Level 9 · class (static)
- What it is: the composition root for the MAUI Blazor Hybrid app.
CreateMauiAppbuilds the DI and configuration graph that runs Blazor components inside a native WebView on Android, iOS, MacCatalyst, and Windows: it loads embedded configuration, registers MudBlazor and the shared UI services, conditionally registers each module's UI, and wires the MAUI-specific auth, form-factor, and device-capability services. - Depends on: the shared registrations
AddUISharedandUseMauiDeviceCapabilities, UIModuleConfiguration, IOAuthUISettings with ConfigurationOAuthUISettings, IHomePageContent, IUIModule with DeviceUIModule, AppActionsInitializer, MauiTokenStorageService for ITokenStorageService, ITokenRefresher with DirectApiTokenRefresher, JwtAuthenticationStateProvider, MauiPublicLinkBuilder, IDeepLinkDispatcher, and App; externals MudBlazor, CommunityToolkit.Maui, theMMCA.Common.UI.Mauipackage, the three module UI packages, andSocketsHttpHandler. - Concept introduced, registration-order-sensitive composition on top of
TryAdddefaults. The shared framework registers safe defaults withTryAdd, so this host must place each override at the right point in the sequence, because a later plainAddwins and aTryAddno-ops once something is present. Three orderings in this file are deliberate and commented:IOAuthUISettingsis registered beforeAddUIShared(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/MauiProgram.cs:62-64) so the shell'sTryAdddefault cannot shadow it;UseMauiDeviceCapabilities()runs afterAddUIShared(:68-71) so its plainAddregistrations override the framework's null capability defaults; and MauiPublicLinkBuilder is registered afterAddConferenceUI()(:88-90) so shared links carry the public web URL rather than the WebView origin. [Rubric §18, UI Architecture] assesses how the UI host is composed, one shared component graph parameterized per platform head. [Rubric §22, Responsive/Cross-Browser] applies because the same Blazor code targets four platforms from this single builder. [Rubric §11, Security] and [Rubric §17, DevOps] both bear on the#if DEBUGblock below. - Walkthrough
- Builder chain (
MauiProgram.cs:36-46):UseMauiApp<App>(),UseMauiCommunityToolkit()(required by the speech-to-text capability, ADR-042 Wave 4, and the toolkit analyzer insists the call sits in the app's own chain,:39-40), a font registration, andConfigureEssentials(essentials => essentials.OnAppAction(HandleAppAction))for home-screen quick actions (:43-46). - Configuration (
MauiProgram.cs:48-57): MAUI does not auto-load config files, so the executing assembly'sMMCA.ADC.UI.appsettings.jsonmanifest resource stream is read and added throughAddJsonStream, guarded by a null check on the stream. - Core UI services:
AddMauiBlazorWebView()andAddMudServices()(:59-60), then the pre-sharedIOAuthUISettingsoverride (:64),AddUIShared(builder.Configuration)(:66),UseMauiDeviceCapabilities()(:71, which also wiresPlugin.LocalNotificationand the notification-tap deep-link bridge), and theIHomePageContentlanding content (:73). - Module registration (
MauiProgram.cs:75-86): fourifblocks gated onUIModuleConfiguration.IsModuleEnabled(builder.Configuration, "...")add the Identity, Conference, Engagement, and Notification UI packages. - Head-specific overrides and services: the public-link override (
:90), the MAUI-only DeviceUIModule (:94) and AppActionsInitializer (:95), the auth stack of MauiTokenStorageService, DirectApiTokenRefresher, JwtAuthenticationStateProvider andAddAuthorizationCore()(:98-101), andAddMauiFormFactor()(:104). - The
#if DEBUGblock (MauiProgram.cs:106-126) adds Blazor WebView developer tools and debug logging, then replaces theAPIClientprimary handler with aSocketsHttpHandlerwhoseRemoteCertificateValidationCallbackalways returns true (:117-124), so the app can reach the WebAPI over LAN using the localhost dev cert. TheS4830/CA5359suppression is scoped to Debug and explained inline (:110-116): Android's native SSL layer rejects the dev cert at the Java level before the managed callback would ever fire, so the managed TLS stack is used instead. HandleAppAction(AppAction action)(MauiProgram.cs:131-144) maps the action id to a route viaAppActionsInitializer.RouteFor(:133), returns when the id is unknown (:134-137), and otherwise publishes the route into IDeepLinkDispatcher (:141-143), which buffers it on cold start.
- Builder chain (
- Why it's built this way: the embedded-resource config load is forced by MAUI's lack of on-disk config discovery, and the ordering comments encode real
TryAdd-versus-Addprecedence rules that are easy to break silently during a refactor. Scoping the certificate bypass to#if DEBUGkeeps an intentionally insecure LAN convenience out of every shipped build. - Where it's used: called by every platform head, MainApplication on Android (
MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/MainApplication.cs:17), AppDelegate on iOS and MacCatalyst (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/iOS/AppDelegate.cs:19), and the WindowsApp(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Windows/App.xaml.cs:16). It is the one place all UI DI is assembled for the mobile and desktop shells. - Caveats / not-in-source: which modules are actually enabled depends on the embedded
appsettings.jsonvalues read byUIModuleConfiguration.IsModuleEnabled; those runtime values are not determinable from this file.
AppDelegate
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/iOS/AppDelegate.cs:16· Level 10 · class
- What it is: the iOS and MacCatalyst application delegate. It boots MAUI by returning MauiProgram's app, and it receives Universal Links (ADR-043): https URLs on the public web host arrive through
ContinueUserActivityand are published to the shared deep-link dispatcher for in-app navigation. - Depends on: MauiProgram, IDeepLinkDispatcher, MAUI's
MauiUIApplicationDelegate, andFoundation/UIKit. - Concept introduced, iOS Universal Links next to Android App Links. The product concept matches MainActivity's App Links but the plumbing differs: iOS delivers the tapped web URL as an
NSUserActivityof typeBrowsingWeb, and the app must carry the associated-domains entitlement plus a liveapple-app-site-associationfile on that host (class summary,MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/iOS/AppDelegate.cs:9-13). [Rubric §25, Navigation & IA] applies: deep links resolve to in-app routes on iOS exactly as on Android, through the same dispatcher. - Walkthrough:
CreateMauiApp(AppDelegate.cs:19) delegates toMauiProgram.CreateMauiApp().ContinueUserActivity(:22-40) checks for aBrowsingWebactivity with a non-nullWebPageUrl(:27-28), reassemblespathplus optional?query(:31), and when the result is non-blank publishes it through IDeepLinkDispatcher and returnstrue(:32-36); every other case defers to the base implementation (:39). The[Register("AppDelegate")]attribute (:15) is what makes the type visible to the Objective-C runtime. - Why it's built this way: mirroring the Android deep-link path through one shared dispatcher means the in-app navigation logic is written once in the shared
DeepLinkListener, and each platform delegate only translates its native event into a route string. - Where it's used: Program passes this type to
UIApplication.Main(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/iOS/Program.cs:11); the MacCatalyst head uses the same delegate.
MainApplication
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/MainApplication.cs:10· Level 10 · class
- What it is: the Android
MauiApplicationsubclass, the process-level Android application object that boots MAUI by returning MauiProgram's app. - Depends on: MauiProgram; MAUI's
MauiApplicationand the Android runtime interop typesIntPtrandJniHandleOwnership. - Concept introduced: reuses the per-platform bootstrapper pattern (see App), where each platform provides a thin entry that calls the shared
MauiProgram. No new concept. - Walkthrough: the
[Application]attribute (MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Android/MainApplication.cs:9) marks it as the Android application class. The(IntPtr handle, JniHandleOwnership ownership)constructor (:12-15) is the JNI-marshalling constructor the Android runtime requires and simply forwards to the base.CreateMauiApp(:17) delegates toMauiProgram.CreateMauiApp(). - Why it's built this way: Android instantiates the application object before any activity, so this is the earliest point where MAUI can be created; keeping it a one-line delegate concentrates composition in MauiProgram.
- Where it's used: the Android runtime instantiates it at process start; it constructs the DI graph that MainActivity and NowNextWidgetProvider later resolve services from via
IPlatformApplication.Current.
Program
MMCA.ADC.UI ·
MMCA.ADC.UI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/iOS/Program.cs:8· Level 11 · class (static)
- What it is: the iOS native entry point.
Mainlaunches the UIKit application with AppDelegate as the delegate type. - Depends on: AppDelegate;
UIKit.UIApplication. - Concept introduced, the iOS managed
Main. Unlike Android, where the OS instantiates MainApplication, iOS starts from a classicMain.UIApplication.Main(args, null, typeof(AppDelegate))(MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/iOS/Program.cs:10-11) hands control to UIKit and names the delegate that will callCreateMauiApp. [Rubric §22, Responsive/Cross-Browser] applies loosely: one codebase, one launcher per platform. - Walkthrough: a single static
Main(string[] args)(Program.cs:10-11) and no other members. - Why it's built this way: the MAUI iOS template requires an explicit
Mainthat names theAppDelegate; there is nothing app-specific to customize here. - Where it's used: the iOS process entry point; it never runs on the other platform heads.
ADCEventInfo
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:178· Level 0 · record
- What it is: A private, sealed, deserialization-only projection of the public events API response, holding the fields the landing page needs to drive its countdown and venue block. It is not shared with any other page.
- Depends on: BCL only:
int,string,DateOnly(ADCHome.razor.cs:178-186). Consumed inside ADCHome and wrapped by ADCCollectionResult. - Concept introduced: Page-local API projection. Rather than reuse a shared event DTO, the page declares its own positional record with exactly the fields it reads, so the anonymous
eventsendpoint's shape stays private to this one component.[Rubric §18, UI Architecture], which assesses how UI concerns are structured: here the page owns a narrow read model instead of leaking a cross-cutting contract. - Walkthrough: Eight positional members (
ADCHome.razor.cs:179-186):Id,Name,Description?,StartDate,EndDate,TimeZone,VenueAddress?,VenueMapUrl?.StartDate/EndDate/TimeZonefeedUpdateCountdown()'s time-zone conversion (ADCHome.razor.cs:121-155);VenueAddress(with its own fallback) builds the Google Maps search URL (ADCHome.razor.cs:40-43). The nullable members allow a partial or absent event to fall back to hardcoded defaults. - Why it's built this way: Keeping the projection private prevents accidental reuse and keeps the wire shape a page detail. Its deserialization is driven by
JsonSerializerDefaults.Weboptions (ADCHome.razor.cs:15). - Where it's used: Deserialized in
LoadEventAsync()viaGetFromJsonAsync<ADCCollectionResult>(ADCHome.razor.cs:80), then reduced to a single featured event by CurrentEventSelector.
ConferenceTrackInfo
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:285· Level 0 · record
- What it is: A private, sealed record for one conference track in the static track catalog rendered on the landing page.
- Depends on: BCL
stringonly (ADCHome.razor.cs:285). Icon values come from MudBlazor'sIcons.Material.*constants (NuGet). - Concept introduced: Hardcoded editorial content records. The track grid changes at most once per conference year, so the twelve tracks are declared as a
private static readonlyarray (ADCHome.razor.cs:203-229) rather than fetched. This is the static half of the page's two-tier content model (see ADCEventInfo for the dynamic half).[Rubric §18, UI Architecture]: content that rarely changes is compiled in, avoiding a CMS dependency. - Walkthrough: Three positional members (
ADCHome.razor.cs:285):Name,Icon(a MudBlazor icon constant such asIcons.Material.Filled.Psychology,ADCHome.razor.cs:205), andTopics(a comma-separated description string). Each element of theTracksarray is passed to<MudIcon>and the track card in the Razor markup. - Why it's built this way: A code-resident catalog keeps the track grid rendering instantly and independent of the API. An
i18n: allowcomment (ADCHome.razor.cs:189-190) documents that this English-only editorial data is intentionally not localized while the UI chrome around it is. - Where it's used: The
Tracksstatic array in ADCHome (ADCHome.razor.cs:203).
EventPhase
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:29· Level 0 · enum
- What it is: A private enum with three states,
Upcoming,Live, andEnded, that classifies where the conference sits relative to now and drives the countdown timer's behavior. - Depends on: Nothing (
ADCHome.razor.cs:29-34). - Concept introduced: Derived UI state as an enum. The phase is not stored; it is recomputed every tick from the event window, keeping render logic a pure function of the current time.
[Rubric §19, State Management], which assesses how transient UI state is modeled: here a single enum field replaces several booleans and makes the render branch exhaustive. - Walkthrough:
UpdateCountdown()sets_phase(ADCHome.razor.cs:146-154): if the start instant is still in the future it isUpcoming; otherwiseLivewhile now is before the computed end instant, elseEnded.OnTimerTickstops the one-second timer via_countdownTimer?.Change(-1, -1)once the phase reachesEnded(ADCHome.razor.cs:114-118), andOnInitializedAsyncnever starts the timer if the event already ended (ADCHome.razor.cs:69-72). - Why it's built this way: Encoding the countdown lifecycle as one value keeps timer start/stop decisions and template branches in sync and avoids ticking forever after the conference ends.
- Where it's used: The
_phasefield of ADCHome.
KeynoteSpeakerInfo
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:284· Level 0 · record
- What it is: A private, sealed record carrying the static keynote-speaker content (name, title, talk title, and multi-paragraph bio) for the landing page's keynote section.
- Depends on: BCL
string/string[]only (ADCHome.razor.cs:284). - Concept introduced: Same hardcoded editorial-content pattern as ConferenceTrackInfo; the single keynote is a
private static readonlyinstance (ADCHome.razor.cs:191-201). - Walkthrough: Four positional members (
ADCHome.razor.cs:284):Name,Title,TalkTitle, andBioParagraphs(string[]). Modeling the bio as an array lets the Razor template render each paragraph in its own element rather than splitting one blob of text. - Why it's built this way: The keynote changes once per conference cycle, so it lives in code alongside the tracks and sponsors under the same
i18n: alloweditorial exemption (ADCHome.razor.cs:189-190). - Where it's used: The
Keynotestatic field in ADCHome (ADCHome.razor.cs:191).
SponsorInfo
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:287· Level 0 · record
- What it is: A private, sealed record for a single sponsor: display name, logo URL, and link URL.
- Depends on: BCL
stringonly (ADCHome.razor.cs:287). - Concept introduced: Same hardcoded editorial-content pattern as ConferenceTrackInfo. Because the logo and link URLs are literal external constants, the whole
SponsorTiersarray carries a documentedS1075suppression (URIs should not be hardcoded) atADCHome.razor.cs:231, a deliberate analyzer exception, not a smell. - Walkthrough: Three positional members (
ADCHome.razor.cs:287):Name,LogoUrl,Url. Instances are nested inside each SponsorTierInfo in theSponsorTiersarray (ADCHome.razor.cs:232-258); tiers with no confirmed sponsors carry an empty list (Gold and Silver,ADCHome.razor.cs:249-250). - Where it's used: Nested within SponsorTierInfo and rendered by ADCHome.
ADCCollectionResult
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:176· Level 1 · record
- What it is: A private, sealed record that is the deserialization envelope for the events API, wrapping the returned list of ADCEventInfo.
- Depends on: ADCEventInfo (
ADCHome.razor.cs:176), plus BCLList<T>. It is Level 1 because it composes the Level-0 projection. - Concept introduced: Collection envelope projection. The public collection endpoint returns an object with an
Itemsarray rather than a bare array, so the page declares a matching one-member wrapper.[Rubric §9, API & Contract Design], which assesses how contracts are shaped at boundaries: the client mirrors the server envelope narrowly instead of importing a shared paged-result type. - Walkthrough: A single positional member,
List<ADCEventInfo>? Items(ADCHome.razor.cs:176).LoadEventAsync()deserializes into it and coalesces a nullItemsto an empty array before selection (ADCHome.razor.cs:80-89). - Why it's built this way: A dedicated envelope keeps deserialization total: a missing or empty payload flows through as an empty list and the page falls back to its hardcoded defaults rather than throwing.
- Where it's used: The
GetFromJsonAsync<ADCCollectionResult>call inside ADCHome (ADCHome.razor.cs:80).
SponsorTierInfo
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:286· Level 1 · record
- What it is: A private, sealed record grouping sponsors into a named, color-coded tier (for example Platinum, Gold, Silver, Swag) for the sponsor wall.
- Depends on: SponsorInfo via
IReadOnlyList<SponsorInfo>(ADCHome.razor.cs:286), plus BCLstring. Composing the Level-0 sponsor record makes this Level 1. - Concept introduced: Same hardcoded editorial-content pattern as ConferenceTrackInfo, one level up: a tier owns its own list of sponsors so the wall can render tier headings and swatches.
- Walkthrough: Three positional members (
ADCHome.razor.cs:286):Name,Color(a hex swatch such as#E5E4E2), andSponsors. TheSponsorTiersarray declares four tiers (ADCHome.razor.cs:232-258). Two helpers key off the tier:GetTierIconmaps the tier name to a MudBlazor icon (ADCHome.razor.cs:260-267), andGetTierHeadingbuilds a localized "<tier> <label>" heading, choosing singular/plural and sponsor/partner wording for the Swag tier via a tuple switch and resource lookups (ADCHome.razor.cs:271-282). - Why it's built this way:
GetTierHeadingruns the visible tier labels through theL[...]localizer (ADR-027) so word order and pluralization follow the selected language, while the sponsor names themselves stay editorial content (ADCHome.razor.cs:269-270).[Rubric §27, i18n], which assesses localization readiness: the chrome around the sponsor data is localized even though the data is not. - Where it's used: The
SponsorTiersstatic array in ADCHome (ADCHome.razor.cs:232).
ADCHome
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHome.razor.cs:13· Level 7 · class
- What it is: The sealed partial code-behind for the ADC conference landing page: a hero with a live countdown, the keynote section, the twelve-track grid, the sponsor wall, and the venue/location block. It fetches the featured event to drive the countdown and composes all the record types above.
- Depends on: ADCCollectionResult, ADCEventInfo, EventPhase, KeynoteSpeakerInfo, ConferenceTrackInfo, SponsorTierInfo, SponsorInfo (all declared in this file), and CurrentEventSelector from the Conference shared layer (
ADCHome.razor.cs:84). Externals:IHttpClientFactory,System.Threading.Timer,TimeZoneInfo,System.Text.Json(BCL), MudBlazorIcons, and the injectedLlocalizer (ADCHome.razor.cs:1-5). - Concept introduced: Timer-driven Blazor countdown with disciplined disposal. The page is a self-contained stateful component: it opens a
CancellationTokenSource, loads data, then ticks a one-secondTimer, and it implementsIDisposableto tear all of that down.[Rubric §19, State Management], which assesses lifecycle and transient state: the component cancels its in-flight fetch, stops and disposes the timer, and guards ticks with a_disposedflag so a late callback cannot touch a torn-down component (ADCHome.razor.cs:106-119,166-173).[Rubric §18, UI Architecture]: the two-tier content model (fetched event versus hardcoded editorial data) keeps the page rendering even when the API is slow or unavailable. - Walkthrough:
- Fields (
ADCHome.razor.cs:15-27): staticApiJsonOptions(web JSON defaults) andEventStartTime(08:00), plus per-instance_cts,_countdownTimer,_timeRemaining,_phase,_event,_isLoading, and_disposed. OnInitializedAsync(ADCHome.razor.cs:64-73): creates the_cts, awaitsLoadEventAsync, and starts the one-second timer only when the phase is not alreadyEnded.LoadEventAsync(ADCHome.razor.cs:75-104): fetcheseventsthrough the namedAPIClient, then callsCurrentEventSelector.SelectCurrentOrNext(...)to feature the live-or-next event instead of an arbitrary first item; it swallowsOperationCanceledException(disposed mid-load) andHttpRequestException(API down) so the fallback defaults render, and always clears_isLoadingand refreshes the countdown infinally.UpdateCountdown(ADCHome.razor.cs:121-155): converts the event's local start/end (using itsTimeZone, defaulting toAmerica/New_York) to UTC viaTimeZoneInfo, falling back to treating the values as UTC onTimeZoneNotFoundException, then sets_timeRemainingand_phase.OnTimerTick(ADCHome.razor.cs:106-119): returns immediately if disposed, recomputes the countdown, marshals a re-render withInvokeAsync(StateHasChanged), and halts the timer onceEnded.- Presentation helpers:
HeroTitlePartssplits the event name to accent the keyword between "Atlanta " and " Conference" (ADCHome.razor.cs:50-62);FormatEventDateformats the date with a resource-supplied pattern underCurrentCulture(ADCHome.razor.cs:157-164);GetTierIconandGetTierHeadingback the sponsor wall (ADCHome.razor.cs:260-282). Dispose(ADCHome.razor.cs:166-173): sets_disposed, cancels and disposes the_cts, and stops and disposes the timer.
- Fields (
- Why it's built this way: Cancellation plus the
_disposedguard prevent the classic Blazor bug where a background timer or an in-flight HTTP call calls back into a disposed component. Routing event selection through the sharedCurrentEventSelectorkeeps the "which event is featured" rule identical across clients (an unorderedFirstOrDefaultwould pin the oldest seeded event,ADCHome.razor.cs:82-83). Formatting and tier headings go through theL[...]localizer so the chrome honors ADR-027 while the editorial content stays English-only. - Where it's used: Exposed as the home component through ADCHomePageContent, and referenced by its WebAssembly twin in
MMCA.ADC.UI.Web.Client(a structural duplicate compiled for the browser render path). - Caveats / not-in-source: The Razor markup (
ADCHome.razor) is not part of this unit; this code-behind file is the ground truth for the page's data and lifecycle logic.
ADCHomePageContent
MMCA.ADC.UI ·
MMCA.ADC.UI.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Pages/ADCHomePageContent.cs:8· Level 8 · class
- What it is: A sealed adapter that plugs the ADC landing page into the shared host shell by implementing the framework's home-page content contract: it names the component type and the page title.
- Depends on: IHomePageContent from
MMCA.Common.UI(ADCHomePageContent.cs:1,8) and ADCHome viatypeof(ADCHomePageContent.cs:10). - Concept introduced: App-supplied content for a shared shell. The shared
Home.razorshell does not hardcode any app's landing page; each app registers anIHomePageContentthat tells the shell which component to render and what title to show.[Rubric §2, Design Patterns], which assesses pattern use at boundaries: this is a small strategy/adapter that keeps the shell app-agnostic and the ADC-specific landing page in the ADC UI project. - Walkthrough: Two get-only members:
ComponentType => typeof(ADCHome)(ADCHomePageContent.cs:10) hands the shell the component to render, andPageTitle => "Atlanta Developers Conference"(ADCHomePageContent.cs:12) supplies the title (ani18n: allowbrand name). - Why it's built this way: Inverting the dependency (app implements the Common contract, Common consumes it) lets the same shell host Store, ADC, and Helpdesk without any app reference, satisfying the framework's "build once, compose per app" boundary.
- Where it's used: Registered in the ADC UI host's DI as the
IHomePageContentimplementation, resolved by the sharedHome.razorshell (MAUI host); the WebAssembly client project carries its own structural twin.
ADCEventInfo
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:209· Level 0 · record
- What it is: the private wire-shape the
ADCHomelanding page deserializes each published event into, the subset of the Conference/eventsresponse the countdown and hero actually read (Id,Name,Description,StartDate,EndDate,TimeZone,VenueAddress,VenueMapUrl). - Depends on: nothing first-party (BCL
DateOnly). It is aprivate sealed recordnested insideADCHome. - Concept: a component-local read model.
[Rubric §9, API & Contract Design](assesses tight, purpose-shaped client contracts): rather than pull the full server DTO into the client, the page declares only the eight fields it renders, so a change to unrelated event fields cannot break the landing page.[Rubric §18, UI Architecture](a page owns its own view model). - Walkthrough: a positional record with eight components (
ADCHome.razor.cs:209-217).Description,VenueAddress, andVenueMapUrlare nullable so the fallback getters on the page can substitute defaults;StartDate/EndDateareDateOnlyandTimeZonea string, the three inputsUpdateCountdownneeds to compute UTC boundaries (ADCHome.razor.cs:161-171). - Why it's built this way: keeping it
privateto the page means it is an implementation detail, never a shared contract;System.Text.Json(JsonSerializerDefaults.Web) binds it by property name off theeventspayload. - Where it's used: the element type of
ADCCollectionResult.Items, selected down to one featured event byCurrentEventSelectorinADCHome.LoadEventAsync(ADCHome.razor.cs:133-142).
ConferenceTrackInfo
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:316· Level 0 · record
- What it is: a three-field content record (
Name,Icon,Topics) describing one conference track card on the landing page. - Depends on: nothing first-party;
Iconvalues come from MudBlazor'sIcons.Material.Filled.*constants (a NuGet string catalog). - Concept: static editorial content held in code.
[Rubric §18, UI Architecture]and[Rubric §27, Internationalization]: the twelveTracksentries (ADCHome.razor.cs:234-260) are the same English-only catalog the API would serve, marked// i18n: allowat the array declaration (ADCHome.razor.cs:220-221); the localized chrome wraps this content rather than translating it. - Walkthrough: a positional
private sealed record(ADCHome.razor.cs:316).Iconcarries a MudBlazor material-icon path string (e.g.Icons.Material.Filled.Psychology);Topicsis a single comma-joined string rendered under each track name. - Where it's used: the static
Tracksarray field onADCHome(ADCHome.razor.cs:234), rendered by the page's Razor markup.
EventPhase
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:30· Level 0 · enum
- What it is: the three-state lifecycle the landing page uses to decide what to show relative to the featured event's start/end:
Upcoming,Live,Ended. - Depends on: nothing first-party. It is a
private enumnested inADCHome. - Concept: UI state as an explicit enum rather than a tangle of booleans.
[Rubric §19, State Management](assesses a single, legible source of view state): the page computes one_phasefield from the clock and branches its hero, countdown, and timer arming off it instead of scatteringnow < startcomparisons through the markup. - Walkthrough: three members,
Upcoming,Live,Ended(ADCHome.razor.cs:32-34). It is assigned in exactly one place, thenow switchinsideUpdateCountdown(ADCHome.razor.cs:180-185): before start isUpcoming, before end isLive, otherwiseEnded.ArmPhaseTimerForEventEndonly schedules its one-shot re-render while_phase == EventPhase.Live(ADCHome.razor.cs:102). - Where it's used: entirely within
ADCHome; it never crosses the component boundary.
KeynoteSpeakerInfo
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:315· Level 0 · record
- What it is: a four-field content record for the featured keynote (
Name,Title,TalkTitle,BioParagraphs), whereBioParagraphsis astring[]rendered as the multi-paragraph abstract. - Depends on: nothing first-party.
- Concept: see
ConferenceTrackInfo, the same static-editorial-content pattern. The singleKeynoteinstance (ADCHome.razor.cs:222-232) is// i18n: allowconference content, not localized chrome. - Walkthrough: a positional
private sealed record(ADCHome.razor.cs:315);BioParagraphsis an array so the markup can emit one block per paragraph without splitting a single string. - Where it's used: the static
Keynotefield onADCHome(ADCHome.razor.cs:222).
SponsorInfo
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:318· Level 0 · record
- What it is: a single sponsor's display data:
Name,LogoUrl, and the outboundUrlits logo links to. - Depends on: nothing first-party.
- Concept: the leaf of the sponsor content tree.
[Rubric §26, Front-End Security]touches this lightly: the logo and link URLs are hardcoded external constants (an S1075 analyzer suppression is applied at theSponsorTiersfield,ADCHome.razor.cs:262), a deliberate trade for editorial content that never comes from user input. - Walkthrough: a positional
private sealed record(ADCHome.razor.cs:318), constructed inline inside theSponsorTiersinitializer (e.g.ADCHome.razor.cs:267-278). - Where it's used: held in the
Sponsorslist of eachSponsorTierInfo.
ADCCollectionResult
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:207· Level 1 · record
- What it is: the tiny envelope the page deserializes the
/eventsresponse into, a single nullableItemslist ofADCEventInfo. - Depends on:
ADCEventInfo(Level 0). - Concept: a client-side projection of the framework's paged collection contract.
[Rubric §9, API & Contract Design]: the server returns a richer collection shape, but the page only needsItems, so it declares a one-property record that binds that field and ignores the rest. - Walkthrough:
private sealed record ADCCollectionResult(List<ADCEventInfo>? Items)(ADCHome.razor.cs:207).Itemsis nullable; the caller coalesces it to an empty array before selection (result?.Items ?? [],ADCHome.razor.cs:139). - Where it's used: the type argument of the
GetFromJsonAsync<ADCCollectionResult>("events", …)call inADCHome.LoadEventAsync(ADCHome.razor.cs:133).
AppActionsInitializer
MMCA.ADC.UI ·
MMCA.ADC.UI.Services·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/AppActionsInitializer.cs:16· Level 1 · class (sealed)
- What it is: a MAUI startup service that publishes the three home-screen quick actions (long-press app-icon shortcuts) once the app is built, with localized titles, and maps each action id to an in-app route.
- Depends on:
IMauiInitializeService(the MMCA.Common.UI initialization contract it implements),IStringLocalizer<AppActionsInitializer>(BCL localization),AppActions/AppAction/FeatureNotSupportedException(MAUI Essentials); route constants fromEngagementRoutePathsandNotificationRoutePaths. Its titles/routes feed theIDeepLinkDispatcherpath documented in the class comment. - Concept introduced, native quick actions as a navigation surface.
[Rubric §25, Navigation & IA](assesses first-class entry points into the app): the three shortcuts (happening_now,my_schedule,notifications,AppActionsInitializer.cs:18-20) are OS-level deep-link jump points, not in-app links.[Rubric §27, Internationalization]: their titles are resolved from the resx pair via the injected localizer at build time (AppActionsInitializer.cs:54-56), so the shortcut labels follow the selected language. - Walkthrough
Initialize(IServiceProvider)(AppActionsInitializer.cs:23): null-guards, short-circuits whenAppActions.Current.IsSupportedis false (:27-30), resolves the localizer, then firesSetActionsAsyncfire-and-forget (_ =,:36) so a slow or failing shortcut registration can never block or fail app startup (the inline comment states exactly this,:34-35).RouteFor(string actionId)(AppActionsInitializer.cs:40): aswitchmapping each id to its app-relative route (EngagementRoutePaths.HappeningNow, a literal/conference/sessions?mine=true,NotificationRoutePaths.NotificationInbox), returningnullfor an unknown id. This is the lookup the activation handler inMauiProgramcalls.SetActionsAsync(AppActionsInitializer.cs:48): builds the threeAppActions and callsAppActions.Current.SetAsync, catchingFeatureNotSupportedExceptionbecause some launchers report support then reject at runtime (:60-64).
- Why it's built this way: registration (titles) and activation (routing) are split, the initializer sets the shortcuts,
MauiProgramwiresOnAppActiontoRouteFor, and both publish the resolved route to the shared deep-link dispatcher, which buffers cold-start activations until the listener renders (class comment,AppActionsInitializer.cs:9-15). ADR-042 (the MAUI head) is the governing decision. - Where it's used: registered as an
IMauiInitializeServicein the MAUI head's DI; itsRouteForis invoked by theConfigureEssentials(e => e.OnAppAction(...))handler inMauiProgram.
MauiPublicLinkBuilder
MMCA.ADC.UI ·
MMCA.ADC.UI.Services·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/MauiPublicLinkBuilder.cs:13· Level 1 · class (sealed)
- What it is: the MAUI implementation of
IPublicLinkBuilder: it turns a relative path into an absolute URL rooted at the public web app, so a shared or copied link points at the public site, not the WebView's internal origin. - Depends on:
IPublicLinkBuilder(implements),IConfiguration(BCL),System.Uri. - Concept: platform-specific override of a UI service.
[Rubric §26, Front-End Security]/[Rubric §25, Navigation & IA]: on the browser head the default builder resolves against the current origin, but a MAUI WebView's origin is an internal shell address that is meaningless when pasted elsewhere, so this head substitutes the pinned public base URL. It is registered after the module registrations inMauiProgramso last-registration-wins replaces the browser default (class comment,MauiPublicLinkBuilder.cs:6-12). - Walkthrough
- Constructor (
MauiPublicLinkBuilder.cs:18): readsPublicSite:BaseUrlfrom the embedded appsettings and throwsInvalidOperationExceptionif it is missing or blank (:20-23), a fail-fast so a misconfigured build cannot silently emit broken share links. The same mechanism pins the gateway endpoint. BuildAbsolute(string relativePath)(MauiPublicLinkBuilder.cs:27): guards against a null/blank path, then combines it onto_baseUrlvia theUri(baseUri, relative)constructor (:29-31).
- Constructor (
- Where it's used: injected wherever the Conference UI builds shareable links (the
IPublicLinkBuilderconsumers in Group 21); only the MAUI head registers this variant.
MauiTokenStorageService
MMCA.ADC.UI ·
MMCA.ADC.UI.Services·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Services/MauiTokenStorageService.cs:9· Level 1 · class (sealed)
- What it is: the MAUI implementation of
ITokenStorageService, persisting the JWT access and refresh tokens in the platform secure enclave via MAUISecureStorage. - Depends on:
ITokenStorageService(implements), MAUISecureStorage(Essentials). - Concept introduced, at-rest token protection on the device head.
[Rubric §11, Security]and[Rubric §26, Front-End Security](assess where credentials live at rest): the browser client keeps tokens in the circuit/local storage, but the MAUI head can do better,SecureStorageroutes to the platform-specific secure enclave (Android Keystore, iOS Keychain, Windows DPAPI, per the class comment,MauiTokenStorageService.cs:5-8), so tokens are encrypted at rest by the OS. - Walkthrough: two fixed key constants (
auth_access_token,auth_refresh_token,MauiTokenStorageService.cs:11-12).GetAccessTokenAsync/GetRefreshTokenAsync(:14,:20) read viaSecureStorage.Default.GetAsync;SetTokensAsync(:26) writes both;ClearTokensAsync(:32) removes both and returnsTask.CompletedTask(the remove call is synchronous). - Why it's built this way: keeping the same
ITokenStorageServiceinterface across heads means the shared auth pipeline is head-agnostic; only the storage backend swaps. SeeTokenStorageDesignNote.mdfor the cross-head storage rationale. - Where it's used: registered as the
ITokenStorageServicein the MAUI head's DI; consumed by the shared auth/token-refresh services in Group 15.
SponsorTierInfo
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:317· Level 1 · record
- What it is: a sponsor tier grouping, its
Name, aColorswatch, and theSponsorslist ofSponsorInfoshown in that band. - Depends on:
SponsorInfo(Level 0). - Concept: the one-to-many parent of the sponsor content tree.
[Rubric §20, Design System & Theming](assesses consistent visual tokens): each tier carries its own hexColorand is paired with a material icon by the page'sGetTierIconswitch (ADCHome.razor.cs:291-298), keeping tier styling data-driven. - Walkthrough:
private sealed record SponsorTierInfo(string Name, string Color, IReadOnlyList<SponsorInfo> Sponsors)(ADCHome.razor.cs:317). The four tiers are declared in the staticSponsorTiersarray (ADCHome.razor.cs:263-289); Gold and Silver ship with empty sponsor lists today. The page turns a tier plus its count into a localized "<tier> <label>" heading viaGetTierHeading(ADCHome.razor.cs:302-313), whose singular/plural and partner-vs-sponsor wording is resource-driven (ADR-027). - Where it's used: the static
SponsorTiersfield onADCHome(ADCHome.razor.cs:263).
ADCHome
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHome.razor.cs:13· Level 7 · class (sealed partial)
- What it is: the code-behind for the ADC conference landing page, a
sealed partialcomponent implementingIDisposablethat fetches the featured published event, drives a countdown timer, and renders the hero, keynote, tracks, sponsors, and venue map. - Depends on:
ADCEventInfo/ADCCollectionResult(its API models),EventPhase, the content recordsKeynoteSpeakerInfo/ConferenceTrackInfo/SponsorTierInfo/SponsorInfo, andCurrentEventSelectorfor the "current-or-next" pick. Externals:IHttpClientFactory,System.Threading.Timer,TimeZoneInfo, MudBlazor, and the injectedIStringLocalizer(L). - Concept introduced, a prerender-safe, render-fenced Blazor page.
[Rubric §23, Front-End Performance](assesses avoiding wasted server work and wasted re-renders). Two decisions stand out. First,OnInitializedAsyncskips the backend fetch during SSR prerender (if (!RendererInfo.IsInteractive),ADCHome.razor.cs:74-79): an un-timed server-side call to a cold or unreachable backend would block the prerender, and therefore the page load and the post-loginNavigateTo("/"), indefinitely, so the static fallback renders now and the interactive pass loads the real event. Second, the per-second ticking lives in a child countdown component (a render fence); this page only arms a single one-shot timer for theLive -> Endedflip (ArmPhaseTimerForEventEnd,ADCHome.razor.cs:100-115) instead of re-rendering the whole page every second for the entire event.[Rubric §29, Resilience & Business Continuity]: a failed fetch (HttpRequestException) or a disposed component (OperationCanceledException) falls through to hardcoded fallback defaults rather than erroring (ADCHome.razor.cs:144-157).[Rubric §27, Internationalization]: user-facing chrome resolves throughL[...]and the date pattern itself is a resource (FormatEventDate,ADCHome.razor.cs:188-195, ADR-027). - Walkthrough
- Fields (
ADCHome.razor.cs:15-28): a staticJsonSerializerOptions(Web defaults), a static 08:00EventStartTime, aCancellationTokenSource, the one-shot_phaseTimer, the computed_startUtc/_endUtc,_phase, the loaded_event, and_isLoading/_disposedflags. - Presentation helpers:
EventName/EventDescription/VenueAddress/MapSearchUrl(ADCHome.razor.cs:37-44) coalesce the loaded event against brand defaults;HeroTitleParts(:51) splits the name to accent the keyword between "Atlanta " and " Conference". OnInitializedAsync(ADCHome.razor.cs:65): creates the CTS, takes the prerender fast path, otherwise awaitsLoadEventAsyncthen arms the end-of-event timer.LoadEventAsync(ADCHome.razor.cs:128): resolves the named"APIClient"HttpClient,GetFromJsonAsync<ADCCollectionResult>("events", …), then narrows to one event withCurrentEventSelector.SelectCurrentOrNext(...)(:137-142) so an unorderedFirstOrDefaultcannot pin the oldest seeded event; thefinallyalways clears_isLoadingand recomputes the countdown.UpdateCountdown(ADCHome.razor.cs:159): converts the event's local start/end (or 2026-10-17 defaults) to UTC viaTimeZoneInfo, falling back to the local values if the zone id is unknown (:173-177), then assigns_phasefrom thenow switch(:180-185).OnCountdownElapsedAsync(:89) andOnEventEnded(:117) recompute and re-render on theUpcoming -> LiveandLive -> Endedtransitions;Dispose(:197) sets_disposed, cancels/disposes the CTS, and disposes the timer.- Static content: the single
Keynote, twelveTracks, and fourSponsorTiers(ADCHome.razor.cs:222-289), plus theGetTierIcon/GetTierHeadinghelpers.
- Fields (
- Why it's built this way: the landing page must render instantly and identically whether the backend is warm, cold, or down, and must never wedge the prerender that gates login navigation; pushing the tick into a child fence and the fetch behind an interactivity check delivers both.
- Where it's used: exposed to the shared
Home.razorshell byADCHomePageContent, which names it as itsComponentType. - Caveats / not-in-source: the per-second countdown rendering and the
OnCountdownElapsedAsynctrigger live in the child countdown component's markup, not in this file; only theLive -> Endedone-shot is wired here.
ADCHomePageContent
MMCA.ADC.UI.Web.Client ·
MMCA.ADC.UI.Web.Client.Pages·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI.Web.Client/Pages/ADCHomePageContent.cs:8· Level 8 · class (sealed)
- What it is: the ADC binding of the framework's
IHomePageContentextension point, it tells the sharedHome.razorshell which component to render as the app's landing page and what browser title to use. - Depends on:
IHomePageContent(implements),ADCHome(the component it points at). - Concept introduced, app-supplied content into a shared shell.
[Rubric §18, UI Architecture]and[Rubric §2, Design Patterns](assess a reusable shell that host apps specialize): the framework ships a genericHome.razorshell; each app registers oneIHomePageContentthat hands the shell aComponentTypeto render and aPageTitle. This inverts the dependency, the shared shell never references the ADC page directly. - Walkthrough: two expression-bodied properties,
ComponentType => typeof(ADCHome)(ADCHomePageContent.cs:10) andPageTitle => "Atlanta Developers Conference"(:12, an// i18n: allowbrand name). No state, no logic. - Why it's built this way: keeping the shell app-agnostic means the Web/WASM heads reuse one landing shell and only the plugged-in content differs per app.
- Where it's used: registered as the
IHomePageContentfor the ADC Web client and resolved by the sharedHome.razorshell in Group 15. - Caveats / not-in-source: the prior tier edition described a paired MAUI
ADCHomePageContentinMMCA.ADC.UI; this unit's source is the single WASM-client class inMMCA.ADC.UI.Web.Client, which references the sameADCHomecomponent.
App
MMCA.ADC.UI ·
MMCA.ADC.UI.WinUI·MMCA.ADC/Source/Hosts/UI/MMCA.ADC.UI/Platforms/Windows/App.xaml.cs:8· Level 10 · class (partial)
- What it is: the WinUI (Windows) platform entry point for the MAUI head, a
partialclass deriving fromMauiWinUIApplicationthat supplies the Windows-specific application object and builds the shared MAUI app. - Depends on:
MauiWinUIApplication(MAUI Windows), andMauiProgram(whoseCreateMauiApp()builds the shared DI/configuration graph). - Concept, the per-platform MAUI bootstrapper.
[Rubric §22, Responsive & Cross-Browser](assesses multiple platform targets from shared code): MAUI centralizes the DI/config graph in oneMauiProgram; each platform provides a thin entry point that calls it. This is the Windows counterpart to the Android/iOS/macOS entry points, each is a few lines that defer to the sameCreateMauiApp(). - Walkthrough: the constructor calls
InitializeComponent()(App.xaml.cs:14, the logicalmain()per its doc comment), and the overrideCreateMauiApp() => MauiProgram.CreateMauiApp()(:16) returns the shared app. Nothing else, all app composition lives inMauiProgram. - Why it's built this way:
partialbecause theApp.xamlmarkup generates the other half; deriving fromMauiWinUIApplicationlets the Windows shell host the cross-platform MAUI app with a minimal per-platform surface. - Where it's used: the WinUI runtime instantiates it as the Windows application object; it is the Windows sibling of the Android/iOS/macOS heads.
- Caveats / not-in-source: the prior tier edition described
Appas inheritingApplicationand holding aMainPagereference; the current WinUI class inheritsMauiWinUIApplicationand only overridesCreateMauiApp()(verified atApp.xaml.cs:8-17).
⬅ ADC Identity Module (Users, Profiles, GDPR Export/Erasure) • Index • Device Capability Abstraction Layer (Native Contracts, MAUI, Browser & Fallback Adapters) ➡