Skip to content

Commit

Permalink
Add CancellationToken Parameter to Methods Returning Task and `Va…
Browse files Browse the repository at this point in the history
…lueTask` (#1503)

* Update .editorconfig

* Update DrawingViewService.windows.cs

* Add `CancellationToken` to IDrawingView, IAlert, AnimationBehavior

* Update AvatarViewGesturesPage

* Update SpeechToTextImplementation.windows.cs

* Update SpeechToTextImplementation.windows.cs

* Add CancellationToken

* Update Unit Tests

* Fix Failing Unit Tests

* Remove Duplicative `ThrowIfCancellationRequested()`

* Remove Duplicate `ThrowIfCancellationRequested()`

* Add Timeout to `async Task` Unit Tests

* Fix Unit Test Timeouts

* Update `AnimateCommand` (`ICommand` -> `Command<CancellationToken> `)

* `dotnet format`

* Fix typo

* Fix compiler bug

* Update DrawingViewService.tizen.cs

* Fix Unit Test Timeout

* Fix BaseTest Inheritance

* Refactor using TaskCompletionSource

* Add CancellationToken

* Increase `TestDuration.Short`

* Increase to TestDuration.Long

* Increase to TestDuration.Medium

* Revert to `ICommand ForceValidateCommand`

* Update ValidationBehavior.shared.cs

* Update `void SeekTo(TimeSpan)`  -> `Task SeekTo(TimeSpan, CancellationToken)`

* Implement `IDisposable` pattern

* Update `PlatformSeek()`

* Promote PlatformSeek to `Task`

* Fix Floating Point Comparison Bug

* Update Windows + Tizen MediaElement

* Update MauiMediaElement.windows.cs

* Refactor + Optimize Code

* Add CancellationToken Unit Tests for `Alerts`, `Animations` and `Behaviors`

* Add CancellationToken Unit Tests for Converters

* Ad Unit Tests for Extensions

* Add Unit Tests for Layouts

* Add CancellationToken for PopupService

* Add Views CancellationToken Unit Tests

* Add Default Value to CancellationToken Parameter on Public APIs

* Update CONTRIBUTING.md

* `dotnet format`

* `dotnet format`

* Update src/CommunityToolkit.Maui/Behaviors/AnimationBehavior.shared.cs

* Update src/CommunityToolkit.Maui/Behaviors/AnimationBehavior.shared.cs
  • Loading branch information
TheCodeTraveler authored Nov 10, 2023
1 parent bd75310 commit 1fce082
Show file tree
Hide file tree
Showing 117 changed files with 2,077 additions and 735 deletions.
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestions
csharp_style_conditional_delegate_call = true:suggestion

# Naming rules

Expand Down Expand Up @@ -122,6 +122,9 @@ dotnet_diagnostic.CA1050.severity = error
# CA2016: Forward the 'cancellationToken' parameter methods that take one
dotnet_diagnostic.CA2016.severity = error

# CA1068: CancellationToken parameters must come last
dotnet_diagnostic.CA1068.severity = error

# CA2208: Method passes parameter as the paramName argument to a ArgumentNullException constructor. Replace this argument with one of the method's parameter names. Note that the provided parameter name should have the exact casing as declared on the method.
dotnet_diagnostic.CA2208.severity = error

Expand Down
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ Here we will have some:
### Debug Logging
* Always use `Trace.WriteLine()` instead of `Debug.WriteLine` for debug logging because `Debug.WriteLine` is removed by the compiler in Release builds

### Methods Returning Task and ValueTask
* Always include a `CancellationToken` as a parameter to every method returning `Task` or `ValueTask`
* If the method is public, provide a the default value for the `CancellationToken` (eg `CancellationToken token = default`)
* If the method is not publc, do not provide a default value for the `CancellationToken`
* Use `CancellationToken.ThrowIfCancellationRequested()` to verify the `CancellationToken`

### Enums
* Always use `Unknown` at index 0 for return types that may have a value that is not known
* Always use `Default` at index 0 for option types that can use the system default option
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,18 @@ async void DisplayCustomSnackbarButtonClicked(object? sender, EventArgs args)
options,
DisplayCustomSnackbarButton);

await customSnackbar.Show();
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await customSnackbar.Show(cts.Token);

DisplayCustomSnackbarButton.Text = dismissCustomSnackbarText;
}
else if (DisplayCustomSnackbarButton.Text is dismissCustomSnackbarText)
{
if (customSnackbar is not null)
{
await customSnackbar.Dismiss();
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await customSnackbar.Dismiss(cts.Token);

customSnackbar.Dispose();
}

Expand Down Expand Up @@ -102,7 +105,7 @@ await Application.Current.MainPage.Navigation.PushModalAsync(new ContentPage

Children =
{
new Button { Command = new AsyncRelayCommand(() => Snackbar.Make("Snackbar in a Modal Page").Show()) }
new Button { Command = new AsyncRelayCommand(token => Snackbar.Make("Snackbar in a Modal Page").Show(token)) }
.Top().CenterHorizontal()
.Text("Display Snackbar"),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ public ToastPage(ToastViewModel toastViewModel) : base(toastViewModel)
async void ShowToastButtonClicked(object? sender, EventArgs args)
{
var toast = Toast.Make("This is a default Toast.");
await toast.Show();

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await toast.Show(cts.Token);
}

async void ShowCustomToastButtonClicked(object? sender, EventArgs args)
{
var toast = Toast.Make("This is a big Toast.", ToastDuration.Long, 30d);
await toast.Show();

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await toast.Show(cts.Token);
}

async void DisplayToastInModalButtonClicked(object? sender, EventArgs e)
Expand All @@ -37,7 +41,7 @@ await Application.Current.MainPage.Navigation.PushModalAsync(new ContentPage

Children =
{
new Button { Command = new AsyncRelayCommand(() => Toast.Make("Toast in a Modal Page").Show()) }
new Button { Command = new AsyncRelayCommand(token => Toast.Make("Toast in a Modal Page").Show(token)) }
.Top().CenterHorizontal()
.Text("Display Toast"),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="AnimationBehavor is a Behavior that can be attached to any VisualElement, allowing developers to: trigger animations via: ICommand AnimateCommand." />
<Span Text="AnimationBehavior is a Behavior that can be attached to any VisualElement, allowing developers to: trigger animations via: ICommand AnimateCommand." />
<Span Text="{x:Static system:Environment.NewLine}" />
<Span Text="* use the default of a TapGestureRecognizer being added to the attached VisualElement" />
<Span Text="{x:Static system:Environment.NewLine}" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ public AnimationBehaviorPage(AnimationBehaviorViewModel animationBehaviorViewMod

class SampleScaleAnimation : BaseAnimation
{
public override async Task Animate(VisualElement view)
public override async Task Animate(VisualElement view, CancellationToken token)
{
await view.ScaleTo(1.2, Length, Easing);
await view.ScaleTo(1, Length, Easing);
await view.ScaleTo(1.2, Length, Easing).WaitAsync(token);
await view.ScaleTo(1, Length, Easing).WaitAsync(token);
}
}

class SampleScaleToAnimation : BaseAnimation
{
public double Scale { get; set; }

public override Task Animate(VisualElement view) => view.ScaleTo(Scale, Length, Easing);
public override Task Animate(VisualElement view, CancellationToken token)
=> view.ScaleTo(Scale, Length, Easing).WaitAsync(token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ async void SetEntryValue(object? sender, EventArgs e)
{
var toastVisibilityTimeSpan = TimeSpan.FromSeconds(5);
var cts = new CancellationTokenSource(toastVisibilityTimeSpan);

await Toast.Make($"The app will crash because `null` is an invalid value for {nameof(NumericValidationBehavior)}.\nOptionally, {nameof(Options)}.{nameof(Options.SetShouldSuppressExceptionsInBehaviors)} can be set to true", Core.ToastDuration.Long).Show(cts.Token);

await Task.Delay(toastVisibilityTimeSpan, cts.Token);

SafeEntry.Text = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,39 @@ public partial class AvatarViewGesturesPage : BasePage<AvatarViewGesturesViewMod
{
public AvatarViewGesturesPage(AvatarViewGesturesViewModel avatarViewGesturesViewModel) : base(avatarViewGesturesViewModel) => InitializeComponent();

async void DragGestureRecognizer_DragStarting(object sender, DragStartingEventArgs e) => await ShowToastGestureMessage("AvatarView drag gesture recognizer, drag starting.");
async void DragGestureRecognizer_DragStarting(object sender, DragStartingEventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await ShowToastGestureMessage("AvatarView drag gesture recognizer, drag starting.", cts.Token);
}

async void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e) => await ShowToastGestureMessage("AvatarView pan gesture recognizer, pan updated.");
async void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await ShowToastGestureMessage("AvatarView pan gesture recognizer, pan updated.", cts.Token);
}

async void PinchGestureRecognizer_PinchUpdated(object sender, PinchGestureUpdatedEventArgs e) => await ShowToastGestureMessage("AvatarView pinch gesture recognizer, pinch updated.");
async void PinchGestureRecognizer_PinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await ShowToastGestureMessage("AvatarView pinch gesture recognizer, pinch updated.", cts.Token);
}

async void SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e) => await ShowToastGestureMessage("AvatarView swipe gesture recognizer, swiped.");
async void SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await ShowToastGestureMessage("AvatarView swipe gesture recognizer, swiped.", cts.Token);
}

async void TapGestureRecognizer_Tapped(object sender, EventArgs e) => await ShowToastGestureMessage("AvatarView Tap Gesture Recognizer, tapped.");
async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await ShowToastGestureMessage("AvatarView Tap Gesture Recognizer, tapped.", cts.Token);
}

static async Task ShowToastGestureMessage(string message)
static Task ShowToastGestureMessage(string message, CancellationToken token)
{
Core.IToast toast = Toast.Make(message, Core.ToastDuration.Short);
await toast.Show();
return toast.Show(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,29 @@ void LoadPointsButtonClicked(object sender, EventArgs e)

async void GetCurrentDrawingViewImageClicked(object sender, EventArgs e)
{
var stream = await DrawingViewControl.GetImageStream(GestureImage.Width, GestureImage.Height);
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var stream = await DrawingViewControl.GetImageStream(GestureImage.Width, GestureImage.Height, cts.Token);

GestureImage.Source = ImageSource.FromStream(() => stream);
}

async void GenerateImageButtonClicked(object sender, EventArgs e)
{
var lines = GenerateLines(2);
await DrawImage(lines);

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await DrawImage(lines, cts.Token);
}

async Task DrawImage(IEnumerable<DrawingLine> lines)
async Task DrawImage(IEnumerable<DrawingLine> lines, CancellationToken token)
{
var drawingLines = lines.ToList();
var points = drawingLines.SelectMany(x => x.Points).ToList();
var stream = await DrawingView.GetImageStream(drawingLines,
new Size(points.Max(x => x.X) - points.Min(x => x.X), points.Max(x => x.Y) - points.Min(x => x.Y)),
Colors.Gray);
Colors.Gray,
token);

GestureImage.Source = ImageSource.FromStream(() => stream);
}

Expand All @@ -71,7 +77,10 @@ async void OnDrawingLineCompleted(object sender, DrawingLineCompletedEventArgs e
{
var width = GetSide(GestureImage.Width);
var height = GetSide(GestureImage.Height);
var stream = await e.LastDrawingLine.GetImageStream(width, height, Colors.Gray.AsPaint());

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var stream = await e.LastDrawingLine.GetImageStream(width, height, Colors.Gray.AsPaint(), cts.Token);

GestureImage.Source = ImageSource.FromStream(() => stream);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ namespace CommunityToolkit.Maui.Sample.Pages.Views.LazyView;

public class CustomLazyView<TView> : Maui.Views.LazyView where TView : View, new()
{
public override async ValueTask LoadViewAsync()
public override async ValueTask LoadViewAsync(CancellationToken token)
{
// display a loading indicator
Content = new ActivityIndicator { IsRunning = true }.Center();

// simulate a long running task
await Task.Delay(3000);
await Task.Delay(3000, token);

// load the view
Content = new TView { BindingContext = BindingContext };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<Label Text="Lazy Loading Based On User Action" Style="{StaticResource Heading}"/>
<Label Text="{Binding Source={x:Reference LazyUserAction}, Path=HasLazyViewLoaded, StringFormat='HasLazyViewLoaded = {0}'}" Style="{StaticResource Heading}"/>
<Button Text="Load View Now" Clicked="LoadLazyView_Clicked" WidthRequest="200" IsVisible="{Binding Source={x:Reference LazyUserAction}, Path=HasLazyViewLoaded, Converter={mct:InvertedBoolConverter}}" />
<local:MyLazyView x:Name="LazyUserAction" Style="{StaticResource MyViewStyle}"/>
<local:MyViewLazyView x:Name="LazyUserAction" Style="{StaticResource MyViewStyle}"/>
</VerticalStackLayout>
</ScrollView>
</pages:BasePage>
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ public LazyViewPage(LazyViewViewModel viewModel) : base(viewModel)
protected override async void OnAppearing()
{
base.OnAppearing();
await LazyActiviation.LoadViewAsync();

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await LazyActiviation.LoadViewAsync(cts.Token);
}

async void LoadLazyView_Clicked(object sender, EventArgs e)
{
await LazyUserAction.LoadViewAsync();
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await LazyUserAction.LoadViewAsync(cts.Token);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using CommunityToolkit.Maui.Views;

namespace CommunityToolkit.Maui.Sample.Pages.Views.LazyView;

class MyViewLazyView : LazyView<MyView>
{
public override async ValueTask LoadViewAsync(CancellationToken token)
{
await base.LoadViewAsync(token);
}
}
Loading

0 comments on commit 1fce082

Please sign in to comment.