Skip to content

MAUI CommunityToolkit MVVM auto instrumentation of async commands #4125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 94 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
228deef
Create MauiGestureRecognizerEventsBinder.cs
aritchie Apr 17, 2025
e66308c
Wireup
aritchie Apr 17, 2025
d2cb84f
Format code
getsentry-bot Apr 17, 2025
2eec158
WIP
aritchie Apr 17, 2025
24248da
Cleanup and fix code due stupid bot
aritchie Apr 17, 2025
98a9789
I'm not an animal - I can save mem
aritchie Apr 17, 2025
35e769c
Format code
getsentry-bot Apr 17, 2025
017f7f1
Command instrumentation
aritchie Apr 17, 2025
006abec
Format code
getsentry-bot Apr 17, 2025
f76f132
Fixes from live usage in our sample app
aritchie Apr 24, 2025
1943f22
Format code
getsentry-bot Apr 24, 2025
6687937
Update according to code review
aritchie Apr 30, 2025
751abe2
Merge branch 'main' into maui_ctmvvm
aritchie Apr 30, 2025
8059884
Update CHANGELOG.md
aritchie Apr 30, 2025
6586050
Update MauiGestureRecognizerEventsBinder.cs
aritchie Apr 30, 2025
d1d5089
Create MauiEventsBinderTests.GestureRecognizers.cs
aritchie Apr 30, 2025
efcd673
Update MauiEventsBinderTests.GestureRecognizers.cs
aritchie Apr 30, 2025
08a82bc
Merge branch 'main' into maui_gestures_breadcrumbs
aritchie Apr 30, 2025
4226806
Initial Unit Tests and samples
aritchie May 1, 2025
5752506
Merge branch 'main' into maui_ctmvvm
aritchie May 1, 2025
054041f
Merge branch 'main' into maui_gestures_breadcrumbs
aritchie May 1, 2025
1fba474
Update MauiEventsBinderTests.cs
aritchie May 1, 2025
118b627
Format code
getsentry-bot May 1, 2025
8ef9aca
Update CHANGELOG.md
aritchie May 1, 2025
559c8e0
Format code
getsentry-bot May 1, 2025
a71375a
WIP
aritchie May 1, 2025
ba54096
Merge branch 'maui_gestures_breadcrumbs' of https://github.com/getsen…
aritchie May 1, 2025
1a74296
add visual runner so I can test
aritchie May 2, 2025
90cbf1d
Merge branch 'main' into maui_ctmvvm
aritchie May 2, 2025
3af183a
Trying to add visual runner
aritchie May 2, 2025
e14e5c2
Merge branch 'main' into maui_gestures_breadcrumbs
aritchie May 2, 2025
66adf29
Update CHANGELOG.md
aritchie May 2, 2025
c55a364
Shelve so I can reset branch
aritchie May 6, 2025
86b25b9
Passing tests
aritchie May 6, 2025
587c137
generate slnf
aritchie May 6, 2025
c76ddec
Format code
getsentry-bot May 6, 2025
4e57733
Merge branch 'main' into maui_ctmvvm
aritchie May 6, 2025
6d8f86f
Finally fixed all build and test issues
aritchie May 6, 2025
7ed7f5c
Update CHANGELOG.md
aritchie May 6, 2025
490ad7b
Comment out visual runner for now
aritchie May 6, 2025
5b15d4d
Update Sentry.Maui.Device.TestApp.csproj
aritchie May 6, 2025
18c6e1f
Format code
getsentry-bot May 6, 2025
fe3fb27
Update device-test.ps1
aritchie May 6, 2025
afc5ea4
Merge branch 'maui_ctmvvm' of https://github.com/getsentry/sentry-dot…
aritchie May 6, 2025
dda6299
Update CtMvvmViewModel.cs
aritchie May 6, 2025
1a1f3b2
Update samples/Sentry.Samples.Maui/MauiProgram.cs
aritchie May 7, 2025
86079a3
Updates from PR review
aritchie May 7, 2025
394d12a
Merge branch 'maui_ctmvvm' of https://github.com/getsentry/sentry-dot…
aritchie May 7, 2025
12760df
Merge branch 'main' into maui_ctmvvm
aritchie May 7, 2025
23a9a00
Update CHANGELOG.md
aritchie May 7, 2025
67dde5f
Updated generated filters - added crashable app - some tests depend o…
aritchie May 7, 2025
f8dfca3
Format code
getsentry-bot May 7, 2025
b0393ac
Fixes
aritchie May 7, 2025
e7a5a9b
Merge branch 'main' into maui_gestures_breadcrumbs
aritchie May 9, 2025
d0c58f4
Bring over device tests to improve setup, fix gesture tests
aritchie May 9, 2025
039e35b
Merge branch 'maui_gestures_breadcrumbs' into maui_ctmvvm
aritchie May 9, 2025
0a08817
Update .generated.NoMobile.sln
aritchie May 9, 2025
4961411
Format code
getsentry-bot May 9, 2025
56e6d12
These run every time in the visual runner
aritchie May 9, 2025
ed4bb20
If working strictly from slnf - these don't get built and are depende…
aritchie May 9, 2025
a79dc8f
Finally got these to generate
aritchie May 9, 2025
ff6c52b
Format code
getsentry-bot May 9, 2025
c8d545c
Update MauiEventsBinderTests.GestureRecognizers.cs
aritchie May 9, 2025
b4f77b1
Merge branch 'maui_gestures_breadcrumbs' of https://github.com/getsen…
aritchie May 9, 2025
c10f2f0
Improve testing setup
aritchie May 9, 2025
19166b5
Merge branch 'maui_gestures_breadcrumbs' into maui_ctmvvm
aritchie May 9, 2025
8fa09e9
Update MauiEventsBinderTests.cs
aritchie May 9, 2025
4c1b584
Update api approval tests... again
aritchie May 9, 2025
28a4993
Update src/Sentry.Maui/Internal/MauiGestureRecognizerEventsBinder.cs
aritchie May 10, 2025
8e32be7
Merge branch 'main' into maui_gestures_breadcrumbs
aritchie May 10, 2025
28c2205
Update MauiGestureRecognizerEventsBinder.cs
aritchie May 10, 2025
44f30f8
Merge branch 'main' into maui_ctmvvm
aritchie May 10, 2025
dd29c1e
Merge branch 'maui_gestures_breadcrumbs' into maui_ctmvvm
aritchie May 10, 2025
bf8888a
Update src/Sentry.Maui.CommunityToolkit.Mvvm/MauiAppBuilderExtensions.cs
aritchie May 12, 2025
e73aa26
Renaming things
aritchie May 12, 2025
5115f0d
Merge branch 'main' into maui_ctmvvm
aritchie May 12, 2025
fd4c0f7
Update Directory.Build.props
aritchie May 12, 2025
e621044
Update MauiEventsBinderTests.cs
aritchie May 12, 2025
0cc439d
Simplified initialisation of the integration
jamescrosswell May 14, 2025
1488ad6
Merge branch 'main' into maui_ctmvvm
jamescrosswell May 14, 2025
62b56c2
Update CHANGELOG.md
jamescrosswell May 14, 2025
6d78d81
Format code
getsentry-bot May 14, 2025
78a934f
Update CtMvvmViewModel.cs
jamescrosswell May 14, 2025
931fb95
Update Sentry.csproj
jamescrosswell May 14, 2025
ecbab28
Update SentryMauiAppBuilderExtensions.cs
jamescrosswell May 14, 2025
52ef07a
Addressed thread safety concerns
jamescrosswell May 14, 2025
a2f3de9
Merge branch 'maui_ctmvvm' of github.com:getsentry/sentry-dotnet into…
jamescrosswell May 14, 2025
fec77a2
Format code
getsentry-bot May 14, 2025
167f9dc
Update MauiCommunityToolkitMvvmEventsBinder.cs
jamescrosswell May 15, 2025
04d35a8
Merge branch 'maui_ctmvvm' of github.com:getsentry/sentry-dotnet into…
jamescrosswell May 15, 2025
f1565c1
Target older version of CommunityToolkit.Mvvm
jamescrosswell May 15, 2025
77cd0cd
Created separate test project for new integration
jamescrosswell May 15, 2025
cb7b258
Workaround stale Scope.Transaction issue
jamescrosswell May 16, 2025
70525e6
Merge branch 'main' into maui_ctmvvm
bruno-garcia May 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
718 changes: 712 additions & 6 deletions .generated.NoMobile.sln

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- .NET MAUI integration with CommunityToolkit.Mvvm Async Relay Commands can now be auto spanned with the new package Sentry.Maui.CommunityToolkit.Mvvm ([#4125](https://github.com/getsentry/sentry-dotnet/pull/4125))

### Dependencies

- Bump Native SDK from v0.8.4 to v0.8.5 ([#4189](https://github.com/getsentry/sentry-dotnet/pull/4189))
Expand Down
2 changes: 2 additions & 0 deletions Sentry-CI-Build-Linux.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj",
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
Expand All @@ -65,6 +66,7 @@
"test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj",
"test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj",
"test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj",
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
Expand Down
2 changes: 2 additions & 0 deletions Sentry-CI-Build-Windows.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj",
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
Expand All @@ -67,6 +68,7 @@
"test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj",
"test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj",
"test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj",
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
Expand Down
2 changes: 2 additions & 0 deletions Sentry-CI-Build-macOS.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj",
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
Expand All @@ -72,6 +73,7 @@
"test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj",
"test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj",
"test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj",
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Device.TestApp\\Sentry.Maui.Device.TestApp.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
Expand Down
1 change: 1 addition & 0 deletions Sentry-CI-CodeQL.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj",
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
Expand Down
718 changes: 712 additions & 6 deletions Sentry.sln

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions SentryMobile.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj",
"src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj",
"src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj",
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj",
"src\\Sentry\\Sentry.csproj",
Expand Down
2 changes: 2 additions & 0 deletions SentryNoSamples.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"src\\Sentry.Google.Cloud.Functions\\Sentry.Google.Cloud.Functions.csproj",
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
Expand All @@ -37,6 +38,7 @@
"test\\Sentry.Google.Cloud.Functions.Tests\\Sentry.Google.Cloud.Functions.Tests.csproj",
"test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj",
"test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj",
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
Expand Down
1 change: 1 addition & 0 deletions samples/Sentry.Samples.Maui/AppShell.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ public partial class AppShell
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute("ctmvvm", typeof(CtMvvmPage));
}
}
18 changes: 18 additions & 0 deletions samples/Sentry.Samples.Maui/CtMvvmPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:maui="clr-namespace:Sentry.Samples.Maui"
x:Class="Sentry.Samples.Maui.CtMvvmPage"
x:DataType="maui:CtMvvmViewModel"
Title="Community Toolkit MVVM">

<ContentPage.BindingContext>
<maui:CtMvvmViewModel />
</ContentPage.BindingContext>

<ContentPage.Content>
<Button Text="Auto-Instrumented Button"
Command="{Binding TestCommand}" />
</ContentPage.Content>
</ContentPage>
16 changes: 16 additions & 0 deletions samples/Sentry.Samples.Maui/CtMvvmPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sentry.Samples.Maui;

public partial class CtMvvmPage : ContentPage
{
public CtMvvmPage()
{
InitializeComponent();
}
}

15 changes: 15 additions & 0 deletions samples/Sentry.Samples.Maui/CtMvvmViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace Sentry.Samples.Maui;

public partial class CtMvvmViewModel : ObservableObject
{
// Sentry automatically creates spans for async relay commands
[RelayCommand]
private async Task Test()
{
var rand = new Random().Next(100, 1000);
await Task.Delay(TimeSpan.FromMilliseconds(rand));
}
}
7 changes: 7 additions & 0 deletions samples/Sentry.Samples.Maui/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@
Clicked="OnFeedbackClicked"
HorizontalOptions="Center" />

<Button
x:Name="CtMvvmBtn"
Text="CommunityToolkit.Mvvm Integration"
SemanticProperties.Hint="Shows a test that auto-instrument async relay commands from the MVVM Community Toolkit."
Clicked="CtMvvmBtn_OnClicked"
HorizontalOptions="Center" />

</VerticalStackLayout>
</ScrollView>

Expand Down
5 changes: 5 additions & 0 deletions samples/Sentry.Samples.Maui/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ private async void OnFeedbackClicked(object sender, EventArgs e)
await Navigation.PushModalAsync(new SubmitFeedback());
}

private void CtMvvmBtn_OnClicked(object sender, EventArgs e)
{
Shell.Current.GoToAsync("ctmvvm");
}

private void TapGestureRecognizer_OnTapped(object sender, TappedEventArgs e)
{
}
Expand Down
9 changes: 9 additions & 0 deletions samples/Sentry.Samples.Maui/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using Sentry.Maui;

namespace Sentry.Samples.Maui;

Expand All @@ -25,6 +26,14 @@ public static MauiApp CreateMauiApp()
options.Debug = true;
options.SampleRate = 1.0F;

// The Sentry MVVM Community Toolkit integration automatically creates traces for async relay commands,
// but only if tracing is enabled. Here we capture all traces (in a production app you'd probably only
// capture a certain percentage)
options.TracesSampleRate = 1.0F;

// Automatically create traces for async relay commands in the MVVM Community Toolkit
options.AddCommunityToolkitIntegration();

#if __ANDROID__
// Currently experimental support is only available on Android
options.Native.ExperimentalOptions.SessionReplay.OnErrorSampleRate = 1.0;
Expand Down
3 changes: 3 additions & 0 deletions samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Sentry.Maui.CommunityToolkit.Mvvm\Sentry.Maui.CommunityToolkit.Mvvm.csproj" />
<ProjectReference Include="..\..\src\Sentry.Maui\Sentry.Maui.csproj" />
<ProjectReference Include="..\..\src\Sentry.Analyzers\Sentry.Analyzers.csproj"
OutputItemType="Analyzer"
Expand All @@ -110,6 +111,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<!-- Check SDK users will be able to resolve the latest version of CommunityToolkit.Mvvm (our Integration targets 8.3.2 -->
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
</ItemGroup>

<!--In order to test our analyzers with this project, we manually reference our targets file. User do not need to do this-->
Expand Down
2 changes: 1 addition & 1 deletion scripts/device-test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ try
{
if (!$Tfm)
{
$Tfm = 'net8.0'
$Tfm = 'net9.0'
}
$arch = (!$IsWindows -and $(uname -m) -eq 'arm64') ? 'arm64' : 'x64'
if ($Platform -eq 'android')
Expand Down
2 changes: 2 additions & 0 deletions scripts/generate-solution-filters-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,12 @@ filterConfigs:
- "src/**/*Bindings.Cocoa.csproj"
- "src/**/Sentry.Extensions.Logging.csproj"
- "src/**/Sentry.Maui.csproj"
- "src/**/Sentry.Maui.CommunityToolkit.Mvvm.csproj"
- "test/**/Sentry.Testing.CrashableApp.csproj"
- "test/**/Sentry.Android.AssemblyReader.Tests.csproj"
- "test/**/Sentry.Extensions.Logging.Tests.csproj"
- "test/**/Sentry.Maui.Device.TestApp.csproj"
- "test/**/Sentry.Testing.CrashableApp.csproj"
- "test/**/Sentry.Maui.Tests.csproj"
- "test/**/Sentry.Testing.csproj"
- "test/**/Sentry.Tests.csproj"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System.Windows.Input;
using CommunityToolkit.Mvvm.Input;
using Sentry.Internal;

namespace Sentry.Maui.CommunityToolkit.Mvvm;

/// <summary>
/// Scans all elements for known commands that are implement
/// </summary>
internal class MauiCommunityToolkitMvvmEventsBinder(IHub hub) : IMauiElementEventBinder
{
private const string SpanName = "ctmvvm";
private const string SpanOp = "relay.command";

/// <summary>
/// Binds to the element
/// </summary>
/// <param name="element"></param>
/// <param name="addBreadcrumb"></param>
public void Bind(VisualElement element, Action<BreadcrumbEvent> addBreadcrumb) => Iterate(element, true);

/// <summary>
/// Unbinds from the element
/// </summary>
/// <param name="element"></param>
/// <exception cref="NotImplementedException"></exception>
public void UnBind(VisualElement element) => Iterate(element, false);


private void Iterate(VisualElement element, bool bind)
{
switch (element)
{
case Button button:
TryBindTo(button.Command, bind);
break;

case ImageButton imageButton:
TryBindTo(imageButton.Command, bind);
break;

case CarouselView carousel:
TryBindTo(carousel.CurrentItemChangedCommand, bind);
TryBindTo(carousel.RemainingItemsThresholdReachedCommand, bind);
TryBindTo(carousel.PositionChangedCommand, bind);
break;

case CollectionView collectionView:
TryBindTo(collectionView.RemainingItemsThresholdReachedCommand, bind);
TryBindTo(collectionView.SelectionChangedCommand, bind);
break;

case Entry entry:
TryBindTo(entry.ReturnCommand, bind);
break;

case RefreshView refresh:
TryBindTo(refresh.Command, bind);
break;

case SearchBar searchBar:
TryBindTo(searchBar.SearchCommand, bind);
break;

default:
TryGestureBinding(element, bind);
break;
}
}

private void TryGestureBinding(VisualElement element, bool bind)
{
if (element is IGestureRecognizers gestureRecognizers)
{
foreach (var gestureRecognizer in gestureRecognizers.GestureRecognizers)
{
TryBindTo(gestureRecognizer, bind);
}
}
}

private void TryBindTo(IGestureRecognizer recognizer, bool bind)
{
switch (recognizer)
{
case TapGestureRecognizer tap:
TryBindTo(tap.Command, bind);
break;

case SwipeGestureRecognizer swipe:
TryBindTo(swipe.Command, bind);
break;

case DragGestureRecognizer drag:
TryBindTo(drag.DragStartingCommand, bind); // unlikely to ever be async
TryBindTo(drag.DropCompletedCommand, bind);
break;

case PointerGestureRecognizer pointer:
TryBindTo(pointer.PointerPressedCommand, bind);
TryBindTo(pointer.PointerReleasedCommand, bind);
TryBindTo(pointer.PointerEnteredCommand, bind); // unlikely to ever be async
TryBindTo(pointer.PointerExitedCommand, bind);
break;

// no command bindings on these gestures, so they're left out
// PinchGestureRecognizer
// PanGestureRecognizer
}
}

private void TryBindTo(ICommand? command, bool bind)
{
const string isSubscribedProperty = "IsSubscribed";

if (!bind || command is not IAsyncRelayCommand relayCommand)
{
return;
}

// since events can retrigger binding pickups, this ensures we don't hook up event handlers more than once.
if (!relayCommand.GetFused<bool>(isSubscribedProperty))
{
lock (relayCommand)
{
if (!relayCommand.GetFused<bool>(isSubscribedProperty))
{
relayCommand.PropertyChanged += RelayCommandOnPropertyChanged;
relayCommand.SetFused(isSubscribedProperty, true);
}
}
}
}

private void RelayCommandOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(IAsyncRelayCommand.IsRunning))
{
return;
}

var relay = (IAsyncRelayCommand)sender!;
if (relay.IsRunning)
{
// Note that we may be creating a transaction here and if so we explicitly don't store it on
// Scope.Transaction, because Scope.Transaction is AsyncLocal<T> and MAUI Apps have a global scope. The
// results would be that we would store the transaction on the scope, but it would never be cleared again,
// since the next call to OnPropertyChanged for this RelayCommand will (likely) be from a different thread.
var span = hub.StartSpan(SpanName, SpanOp);
if (span is ITransactionTracer transaction)
{
hub.ConfigureScope(scope => scope.Transaction = transaction);
}

relay.SetFused(span);
}
else if (relay.GetFused<ISpan>() is { } span)
{
span.Finish();
}
}
}
Loading
Loading