diff --git a/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs b/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs index fcbdfafd..1ce16862 100644 --- a/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs +++ b/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs @@ -4,18 +4,31 @@ namespace SalmonEgg.Presentation.Core.ViewModels.Chat.Overlay; internal sealed class ChatConversationSurfaceProjectionCoordinator { + private bool _hasLoadedActiveConversationRoot; + private bool _hasLoadedTranscriptSurface; + public ChatConversationSurfaceProjection Project(ChatConversationSurfaceStateInput input) { var state = ChatConversationSurfaceStatePresenter.Resolve(input); + if (state.ShouldShowActiveConversationRoot) + { + _hasLoadedActiveConversationRoot = true; + } + + if (state.ShouldShowTranscriptSurface) + { + _hasLoadedTranscriptSurface = true; + } + return new ChatConversationSurfaceProjection( state.IsActivationOverlayVisible, state.IsOverlayVisible, state.ShouldShowActiveConversationRoot, - state.ShouldShowActiveConversationRoot, + _hasLoadedActiveConversationRoot, state.ShouldShowSessionHeader, state.ShouldShowTranscriptSurface, - state.ShouldShowTranscriptSurface, + _hasLoadedTranscriptSurface, state.ShouldShowConversationInputSurface, state.ShouldShowBlockingLoadingMask, state.ShouldShowLoadingOverlayStatusPill, diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs index c3f59fed..63ef7404 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs @@ -11367,10 +11367,10 @@ await fixture.UpdateStateAsync(state => state with runtimeState.IsSessionActivationInProgress = true; Assert.False(fixture.ViewModel.ShouldShowActiveConversationRoot); - Assert.False(fixture.ViewModel.ShouldLoadActiveConversationRoot); + Assert.True(fixture.ViewModel.ShouldLoadActiveConversationRoot); Assert.False(fixture.ViewModel.ShouldShowSessionHeader); Assert.False(fixture.ViewModel.ShouldShowTranscriptSurface); - Assert.False(fixture.ViewModel.ShouldLoadTranscriptSurface); + Assert.True(fixture.ViewModel.ShouldLoadTranscriptSurface); Assert.False(fixture.ViewModel.ShouldShowConversationInputSurface); Assert.Equal(SessionNamePolicy.CreateDefault("conv-1"), fixture.ViewModel.CurrentSessionDisplayName); Assert.Equal(string.Empty, fixture.ViewModel.PresentedSessionHeaderDisplayName); @@ -12427,10 +12427,10 @@ await fixture.DispatchConnectionAsync(new SetNewSessionDraftAction(new NewSessio syncContext.RunAll(); Assert.False(fixture.ViewModel.ShouldShowActiveConversationRoot); - Assert.False(fixture.ViewModel.ShouldLoadActiveConversationRoot); + Assert.True(fixture.ViewModel.ShouldLoadActiveConversationRoot); Assert.False(fixture.ViewModel.ShouldShowSessionHeader); Assert.False(fixture.ViewModel.ShouldShowTranscriptSurface); - Assert.False(fixture.ViewModel.ShouldLoadTranscriptSurface); + Assert.True(fixture.ViewModel.ShouldLoadTranscriptSurface); Assert.False(fixture.ViewModel.ShouldShowConversationInputSurface); var switchTask = fixture.ViewModel.SwitchConversationAsync("conv-2"); @@ -12447,10 +12447,10 @@ await fixture.DispatchConnectionAsync(new SetNewSessionDraftAction(new NewSessio syncContext.RunAll(); Assert.False(fixture.ViewModel.ShouldShowActiveConversationRoot); - Assert.False(fixture.ViewModel.ShouldLoadActiveConversationRoot); + Assert.True(fixture.ViewModel.ShouldLoadActiveConversationRoot); Assert.False(fixture.ViewModel.ShouldShowSessionHeader); Assert.False(fixture.ViewModel.ShouldShowTranscriptSurface); - Assert.False(fixture.ViewModel.ShouldLoadTranscriptSurface); + Assert.True(fixture.ViewModel.ShouldLoadTranscriptSurface); Assert.False(fixture.ViewModel.ShouldShowConversationInputSurface); Assert.Equal("conv-2", runtimeState.DesiredSessionId); Assert.True(runtimeState.IsSessionActivationInProgress); diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs index 6dca3376..b1bc7652 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs @@ -50,7 +50,7 @@ public void Project_WhenActiveConversationRootWasShown_KeepsLoadStateWhenLaterHi Assert.True(visible.ShouldShowActiveConversationRoot); Assert.True(visible.ShouldLoadActiveConversationRoot); Assert.False(hidden.ShouldShowActiveConversationRoot); - Assert.False(hidden.ShouldLoadActiveConversationRoot); + Assert.True(hidden.ShouldLoadActiveConversationRoot); } [Fact] @@ -97,6 +97,6 @@ public void Project_WhenTranscriptSurfaceWasShown_KeepsLoadStateWhenLaterHidden( Assert.True(visible.ShouldShowTranscriptSurface); Assert.True(visible.ShouldLoadTranscriptSurface); Assert.False(hidden.ShouldShowTranscriptSurface); - Assert.False(hidden.ShouldLoadTranscriptSurface); + Assert.True(hidden.ShouldLoadTranscriptSurface); } } diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadContextIntentDispatcherSourceTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadContextIntentDispatcherSourceTests.cs index 11a87338..57c3a902 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadContextIntentDispatcherSourceTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadContextIntentDispatcherSourceTests.cs @@ -8,7 +8,7 @@ public sealed class GamepadContextIntentDispatcherSourceTests [Fact] public void MainShellContextDispatcher_UsesFocusedAncestorConsumerPattern() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\MainShellGamepadContextIntentDispatcher.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/MainShellGamepadContextIntentDispatcher.cs"); Assert.Contains("IGamepadContextIntentConsumer", code); Assert.Contains("TryConsumeContextIntent", code); @@ -19,7 +19,7 @@ public void MainShellContextDispatcher_UsesFocusedAncestorConsumerPattern() [Fact] public void WindowsGamepadInputService_MapsTriggersToContextIntentEvents() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\WindowsGamepadInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/WindowsGamepadInputService.cs"); Assert.Contains("GamepadContextIntentProcessor", code); Assert.Contains("ContextIntentRaised", code); @@ -31,7 +31,7 @@ public void WindowsGamepadInputService_MapsTriggersToContextIntentEvents() [Fact] public void WindowsMainPage_BridgesNativeTriggerKeysThroughContextDispatcher() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Platforms\Windows\MainPage.Windows.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Platforms/Windows/MainPage.Windows.cs"); Assert.Contains("case Windows.System.VirtualKey.GamepadLeftTrigger:", code); Assert.Contains("case Windows.System.VirtualKey.GamepadRightTrigger:", code); @@ -45,7 +45,7 @@ public void WindowsMainPage_BridgesNativeTriggerKeysThroughContextDispatcher() [Fact] public void SettingsPageBase_ImplementsContextIntentConsumer() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Views\SettingsPageBase.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Views/SettingsPageBase.cs"); Assert.Contains("IGamepadContextIntentConsumer", code); Assert.Contains("TryConsumeContextIntent", code); @@ -56,7 +56,7 @@ public void SettingsPageBase_ImplementsContextIntentConsumer() public void MainShellContextDispatcher_RetriesFromRootContentWhenFocusedElementIsNotConsumable() { var code = File.ReadAllText( - @"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\MainShellGamepadContextIntentDispatcher.cs"); + "../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/MainShellGamepadContextIntentDispatcher.cs"); Assert.Contains("TryDispatchFromRoot(focused, intent)", code, System.StringComparison.Ordinal); Assert.Contains("TryDispatchFromRoot(GetCurrentRootContent(), intent)", code, System.StringComparison.Ordinal); diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadShortcutDispatcherSourceTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadShortcutDispatcherSourceTests.cs index 4b4601f7..1b3c96cf 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadShortcutDispatcherSourceTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Input/GamepadShortcutDispatcherSourceTests.cs @@ -8,7 +8,7 @@ public sealed class GamepadShortcutDispatcherSourceTests [Fact] public void MainShellShortcutDispatcher_UsesFocusedAncestorShortcutConsumerPattern() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\MainShellGamepadShortcutDispatcher.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/MainShellGamepadShortcutDispatcher.cs"); Assert.Contains("IGamepadShortcutConsumer", code); Assert.Contains("TryConsumeShortcutIntent", code); @@ -19,7 +19,7 @@ public void MainShellShortcutDispatcher_UsesFocusedAncestorShortcutConsumerPatte [Fact] public void WindowsGamepadInputService_MapsYToShortcutEvent_NotNavigationIntent() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\WindowsGamepadInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/WindowsGamepadInputService.cs"); Assert.Contains("GamepadButtons.Y", code); Assert.Contains("ShortcutRaised", code); diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Input/NativeVoiceInputServiceSourceTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Input/NativeVoiceInputServiceSourceTests.cs index 25ad2f39..56c5083e 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Input/NativeVoiceInputServiceSourceTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Input/NativeVoiceInputServiceSourceTests.cs @@ -8,7 +8,7 @@ public sealed class NativeVoiceInputServiceSourceTests [Fact] public void NativeVoiceInputService_DoesNotReuseRecognizerAcrossVoiceSessions() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\NativeVoiceInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/NativeVoiceInputService.cs"); Assert.Contains("CreateSessionRecognizerAsync", code); Assert.DoesNotContain("string.Equals(_activeLanguageTag, normalizedLanguage", code); @@ -17,7 +17,7 @@ public void NativeVoiceInputService_DoesNotReuseRecognizerAcrossVoiceSessions() [Fact] public void NativeVoiceInputService_DisposesRecognizerWhenSessionCompletes() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\NativeVoiceInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/NativeVoiceInputService.cs"); Assert.Contains("DisposeRecognizer();", code); Assert.Contains("finally", code); @@ -26,7 +26,7 @@ public void NativeVoiceInputService_DisposesRecognizerWhenSessionCompletes() [Fact] public void NativeVoiceInputService_UsesGracefulStopBeforeFallingBackToCancel() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\NativeVoiceInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/NativeVoiceInputService.cs"); Assert.Contains(".StopAsync()", code); Assert.Contains(".CancelAsync()", code); @@ -35,7 +35,7 @@ public void NativeVoiceInputService_UsesGracefulStopBeforeFallingBackToCancel() [Fact] public void NativeVoiceInputService_BoundsStopAndCancelWithTimeouts() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\NativeVoiceInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/NativeVoiceInputService.cs"); Assert.Contains("VoiceInputStopTimeout", code); Assert.Contains("VoiceInputCancelTimeout", code); @@ -47,7 +47,7 @@ public void NativeVoiceInputService_BoundsStopAndCancelWithTimeouts() [Fact] public void NativeVoiceInputService_PerformsMicrophonePreflightOnUiThreadBeforeStartingRecognition() { - var code = File.ReadAllText(@"..\..\..\..\..\SalmonEgg\SalmonEgg\Presentation\Services\Input\NativeVoiceInputService.cs"); + var code = File.ReadAllText("../../../../../SalmonEgg/SalmonEgg/Presentation/Services/Input/NativeVoiceInputService.cs"); Assert.Contains("MediaCaptureInitializationSettings", code); Assert.Contains("StreamingCaptureMode = StreamingCaptureMode.Audio", code); diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Input/VoiceInputLoggingSourceTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Input/VoiceInputLoggingSourceTests.cs index 879c1f71..22511cce 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Input/VoiceInputLoggingSourceTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Input/VoiceInputLoggingSourceTests.cs @@ -8,7 +8,7 @@ public sealed class VoiceInputLoggingSourceTests [Fact] public void ChatViewModel_DoesNotDuplicateFirstPartialLoggingOwnedByVoiceService() { - var code = File.ReadAllText(@"..\..\..\..\..\src\SalmonEgg.Presentation.Core\ViewModels\Chat\ChatViewModel.CommandWorkflow.cs"); + var code = File.ReadAllText("../../../../../src/SalmonEgg.Presentation.Core/ViewModels/Chat/ChatViewModel.CommandWorkflow.cs"); Assert.DoesNotContain("Voice input first partial received.", code); }