Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Commit fe2aeb2

Browse files
maxkoshevoijsuarezruizpictosTheCodeTravelerVladislavAntonyuk
authored
Fix DisplaySnackBarAsync Task never completes if another SnackBar appears, and completes before SnackBar action (#1357)
* Update SnackBar.android.cs * Fix #1340 * Pass exception into result * Fix build * Add test * Use tabs * Extract OnActionClick method * Update SnackBar.tizen.cs * Update ToastOptions.shared.cs Make SetResult and SetException internal Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com> Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com> Co-authored-by: Brandon Minnick <13558917+brminnick@users.noreply.github.com> Co-authored-by: Vladislav Antonyuk <33021114+VladislavAntonyuk@users.noreply.github.com> Co-authored-by: Vladislav Antonyuk <vlad.antonyuk@gmail.com>
1 parent f3f1a80 commit fe2aeb2

File tree

13 files changed

+111
-54
lines changed

13 files changed

+111
-54
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<pages:BasePage
3+
x:Class="Xamarin.CommunityToolkit.Sample.Pages.TestCases.SnackBarActionExceptionPage"
4+
xmlns="http://xamarin.com/schemas/2014/forms"
5+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
6+
xmlns:pages="clr-namespace:Xamarin.CommunityToolkit.Sample.Pages">
7+
8+
<ContentPage.Content>
9+
<Grid RowDefinitions="auto, *, *">
10+
<Button Text="App shouldn't crash when you press this and execute action." Clicked="ShowSnackBar" />
11+
</Grid>
12+
</ContentPage.Content>
13+
14+
</pages:BasePage>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using Xamarin.CommunityToolkit.Extensions;
3+
4+
namespace Xamarin.CommunityToolkit.Sample.Pages.TestCases
5+
{
6+
public partial class SnackBarActionExceptionPage
7+
{
8+
public SnackBarActionExceptionPage() => InitializeComponent();
9+
10+
async void ShowSnackBar(object sender, EventArgs e)
11+
{
12+
try
13+
{
14+
await this.DisplaySnackBarAsync("Execute action to throw exception", "Throw exception", () => throw new Exception());
15+
}
16+
catch
17+
{
18+
await this.DisplayToastAsync("Exception caught");
19+
}
20+
}
21+
}
22+
}

samples/XCT.Sample/ViewModels/TestCases/TestCasesGalleryViewModel.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
1212
new SectionModel(
1313
typeof(TouchEffectButtonPage),
1414
"TouchEffect + Button",
15-
"TouchEffect must automatically invoke button'c command execution."),
15+
"TouchEffect must automatically invoke button's command execution."),
1616

1717
new SectionModel(
1818
typeof(TouchEffectCollectionViewPage),
@@ -38,6 +38,11 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
3838
typeof(LinkerCameraViewPage),
3939
"Linker for CameraView",
4040
"Make sure that Linker is keeping the MediaCaptured and MediaCaptureFailed events if they are used."),
41+
42+
new SectionModel(
43+
typeof(SnackBarActionExceptionPage),
44+
"SnackBar Action Exception",
45+
"Exception in SnackBar's action doesn't crash the app."),
4146
};
4247
}
4348
}

src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Snackbar/Helpers/SnackBarLayout.uwp.wpf.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,17 @@ public SnackBarLayout(SnackBarOptions options)
7373
Command = new Forms.Command(async () =>
7474
{
7575
OnSnackBarActionExecuted?.Invoke();
76-
if (action.Action != null)
77-
await action.Action();
76+
try
77+
{
78+
if (action.Action != null)
79+
await action.Action();
80+
81+
options.SetResult(true);
82+
}
83+
catch (Exception ex)
84+
{
85+
options.SetException(ex);
86+
}
7887
}),
7988
Padding = new Thickness(action.Padding.Left,
8089
action.Padding.Top,

src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Snackbar/Options/ToastOptions.shared.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public class ToastOptions
5151
/// </summary>
5252
public TaskCompletionSource<bool> Result { get; }
5353

54-
public void SetResult(bool result) => Result.SetResult(result);
54+
internal bool SetResult(bool result) => Result.TrySetResult(result);
55+
56+
internal bool SetException(Exception exception) => Result.TrySetException(exception);
5557
}
5658
}

src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Snackbar/SnackBar.android.cs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
namespace Xamarin.CommunityToolkit.UI.Views
1717
{
18-
class SnackBar
18+
partial class SnackBar
1919
{
20-
internal async ValueTask Show(VisualElement sender, SnackBarOptions arguments)
20+
internal partial async ValueTask Show(VisualElement sender, SnackBarOptions arguments)
2121
{
2222
var renderer = await GetRendererWithRetries(sender) ?? throw new ArgumentException("Provided VisualElement cannot be parent to SnackBar", nameof(sender));
2323
var snackBar = AndroidSnackBar.Make(renderer.View, arguments.MessageOptions.Message, (int)arguments.Duration.TotalMilliseconds);
@@ -87,11 +87,7 @@ internal async ValueTask Show(VisualElement sender, SnackBarOptions arguments)
8787

8888
foreach (var action in arguments.Actions)
8989
{
90-
snackBar.SetAction(action.Text, async v =>
91-
{
92-
if (action.Action != null)
93-
await action.Action();
94-
});
90+
snackBar.SetAction(action.Text, async _ => await OnActionClick(action, arguments).ConfigureAwait(false));
9591
if (action.ForegroundColor != Forms.Color.Default)
9692
{
9793
snackBar.SetActionTextColor(action.ForegroundColor.ToAndroid());
@@ -154,15 +150,10 @@ public override void OnDismissed(Java.Lang.Object transientBottomBar, int e)
154150
{
155151
base.OnDismissed(transientBottomBar, e);
156152

157-
switch (e)
158-
{
159-
case DismissEventTimeout:
160-
arguments.SetResult(false);
161-
break;
162-
case DismissEventAction:
163-
arguments.SetResult(true);
164-
break;
165-
}
153+
if (e == DismissEventAction)
154+
return;
155+
156+
arguments.SetResult(false);
166157
}
167158
}
168159
}

src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Snackbar/SnackBar.gtk.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
23
using System.Threading.Tasks;
34
using System.Timers;
45
using Gtk;
@@ -10,11 +11,11 @@
1011

1112
namespace Xamarin.CommunityToolkit.UI.Views
1213
{
13-
class SnackBar
14+
partial class SnackBar
1415
{
1516
Timer? snackBarTimer;
1617

17-
public ValueTask Show(VisualElement visualElement, SnackBarOptions arguments)
18+
internal partial ValueTask Show(VisualElement visualElement, SnackBarOptions arguments)
1819
{
1920
var mainWindow = (Platform.GetRenderer(visualElement).Container.Child as Forms.Platform.GTK.Controls.Page)?.Children[0] as VBox;
2021
var snackBarLayout = GetSnackBarLayout(mainWindow, arguments);
@@ -55,14 +56,10 @@ HBox GetSnackBarLayout(Container? container, SnackBarOptions arguments)
5556
button.ModifyBg(StateType.Normal, action.BackgroundColor.ToGtkColor());
5657
button.ModifyFg(StateType.Normal, action.ForegroundColor.ToGtkColor());
5758

58-
button.Clicked += async (sender, e) =>
59+
button.Clicked += async (_, _) =>
5960
{
6061
snackBarTimer?.Stop();
61-
62-
if (action.Action != null)
63-
await action.Action();
64-
65-
arguments.SetResult(true);
62+
await OnActionClick(action, arguments).ConfigureAwait(false);
6663
container?.Remove(snackBarLayout);
6764
};
6865

src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Snackbar/SnackBar.ios.macos.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
namespace Xamarin.CommunityToolkit.UI.Views
1717
{
18-
class SnackBar
18+
partial class SnackBar
1919
{
20-
internal ValueTask Show(VisualElement sender, SnackBarOptions arguments)
20+
internal partial ValueTask Show(VisualElement sender, SnackBarOptions arguments)
2121
{
2222
var snackBar = NativeSnackBar.MakeSnackBar(arguments.MessageOptions.Message)
2323
.SetDuration(arguments.Duration)
@@ -123,11 +123,7 @@ internal ValueTask Show(VisualElement sender, SnackBarOptions arguments)
123123
actionButton.SetAction(async () =>
124124
{
125125
snackBar.Dismiss();
126-
127-
if (action.Action != null)
128-
await action.Action();
129-
130-
arguments.SetResult(true);
126+
await OnActionClick(action, arguments).ConfigureAwait(false);
131127
});
132128

133129
snackBar.Actions.Add(actionButton);

src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Snackbar/SnackBar.netstandard.tvos.watchos.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
namespace Xamarin.CommunityToolkit.UI.Views
77
{
8-
class SnackBar
8+
partial class SnackBar
99
{
10-
internal ValueTask Show(VisualElement sender, SnackBarOptions arguments) => throw new PlatformNotSupportedException();
10+
internal partial ValueTask Show(VisualElement sender, SnackBarOptions arguments) => throw new PlatformNotSupportedException();
1111
}
1212
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Xamarin.CommunityToolkit.UI.Views.Options;
4+
using Xamarin.Forms;
5+
6+
namespace Xamarin.CommunityToolkit.UI.Views
7+
{
8+
partial class SnackBar
9+
{
10+
internal partial ValueTask Show(VisualElement sender, SnackBarOptions arguments);
11+
12+
async Task OnActionClick(SnackBarActionOptions action, SnackBarOptions arguments)
13+
{
14+
try
15+
{
16+
if (action.Action != null)
17+
await action.Action();
18+
19+
arguments.SetResult(true);
20+
}
21+
catch (Exception ex)
22+
{
23+
arguments.SetException(ex);
24+
}
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)