Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ToastService.cs
1using System;
2using System.Collections.Concurrent;
3using System.Threading;
4using System.Threading.Tasks;
5using Microsoft.Extensions.DependencyInjection;
6using Microsoft.Maui.ApplicationModel;
7using Microsoft.Maui.Controls;
9
11{
13 [Singleton]
15 {
16 private readonly ConcurrentQueue<Func<Task>> taskQueue = new();
17 private bool isExecutingQueue;
18 private View? activeToast;
19 private ToastOptions? activeOptions;
20 private CancellationTokenSource? autoHideTokenSource;
21
23 public event EventHandler? ToastChanged;
24
26 public bool HasActiveToast => this.activeToast is not null;
27
29 public Task ShowAsync<TToast>(ToastOptions? Options = null) where TToast : View
30 {
31 TToast Toast = ServiceRef.Provider.GetRequiredService<TToast>();
32 return this.ShowAsync(Toast, Options);
33 }
34
36 public Task ShowAsync(View Toast, ToastOptions? Options = null)
37 {
38 if (Toast is null)
39 throw new ArgumentNullException(nameof(Toast));
40
41 ToastOptions EffectiveOptions = Options ?? new ToastOptions();
42 return this.Enqueue(async () => await this.ShowInternalAsync(Toast, EffectiveOptions));
43 }
44
46 public Task HideAsync()
47 {
48 return this.Enqueue(async () => await this.HideInternalAsync(null, false));
49 }
50
51 private async Task ShowInternalAsync(View Toast, ToastOptions Options)
52 {
53 await this.HideInternalAsync(null, true);
54
55 this.autoHideTokenSource?.Cancel();
56 this.autoHideTokenSource = null;
57
58 this.activeToast = Toast;
59 this.activeOptions = Options;
60
61 IShellPresenter Presenter = this.GetPresenter();
62 await Presenter.ShowToast(Toast, Options.ShowTransition, Options.Placement);
63 this.RaiseToastChanged();
64
65 if (Options.AutoDismiss)
66 this.ScheduleAutoHide(Options);
67 }
68
69 private async Task HideInternalAsync(ToastOptions? Options, bool IsReplacing)
70 {
71 if (this.activeToast is null)
72 return;
73
74 ToastOptions EffectiveOptions = Options ?? this.activeOptions ?? new ToastOptions();
75 this.autoHideTokenSource?.Cancel();
76 this.autoHideTokenSource = null;
77
78 try
79 {
80 IShellPresenter Presenter = this.GetPresenter();
81 await Presenter.HideToast(EffectiveOptions.HideTransition);
82 }
83 catch (Exception Ex)
84 {
85 ServiceRef.LogService.LogException(Ex);
86 }
87
88 this.activeToast = null;
89 this.activeOptions = null;
90
91 if (!IsReplacing)
92 this.RaiseToastChanged();
93 }
94
95 private void ScheduleAutoHide(ToastOptions Options)
96 {
97 CancellationTokenSource TokenSource = new();
98 this.autoHideTokenSource = TokenSource;
99
100 _ = Task.Run(async () =>
101 {
102 try
103 {
104 await Task.Delay(Options.Duration, TokenSource.Token);
105 if (!TokenSource.Token.IsCancellationRequested)
106 await this.Enqueue(async () => await this.HideInternalAsync(Options, false));
107 }
108 catch (TaskCanceledException)
109 {
110 }
111 catch (Exception Ex)
112 {
113 ServiceRef.LogService.LogException(Ex);
114 }
115 });
116 }
117
118 private Task Enqueue(Func<Task> Action)
119 {
120 TaskCompletionSource<bool> Completion = new(TaskCreationOptions.RunContinuationsAsynchronously);
121
122 this.taskQueue.Enqueue(async () =>
123 {
124 try
125 {
126 await Action();
127 Completion.TrySetResult(true);
128 }
129 catch (Exception Ex)
130 {
131 Completion.TrySetException(Ex);
132 }
133 });
134
135 this.StartQueueProcessing();
136 return Completion.Task;
137 }
138
139 private void StartQueueProcessing()
140 {
141 if (this.isExecutingQueue)
142 return;
143
144 this.isExecutingQueue = true;
145 MainThread.BeginInvokeOnMainThread(async () => await this.ProcessQueueAsync());
146 }
147
148 private async Task ProcessQueueAsync()
149 {
150 try
151 {
152 while (this.taskQueue.TryDequeue(out Func<Task>? Action))
153 await Action();
154 }
155 catch (Exception Ex)
156 {
157 ServiceRef.LogService.LogException(Ex);
158 }
159 finally
160 {
161 this.isExecutingQueue = false;
162 }
163 }
164
165 private IShellPresenter GetPresenter()
166 {
167 Page? MainPage = Application.Current?.MainPage;
168 if (MainPage is null)
169 throw new InvalidOperationException("No main page is available for toast presentation.");
170
171 IShellPresenter? Presenter = (MainPage as NavigationPage)?.CurrentPage as IShellPresenter ?? MainPage as IShellPresenter;
172 if (Presenter is null)
173 throw new InvalidOperationException("CustomShell presenter not found.");
174
175 return Presenter;
176 }
177
178 private void RaiseToastChanged()
179 {
180 ToastChanged?.Invoke(this, EventArgs.Empty);
181 }
182 }
183}
Base class that references services in the app.
Definition: ServiceRef.cs:43
static IServiceProvider Provider
The service provider for the app. This is set before the app is started, and will be used to resolve ...
Definition: ServiceRef.cs:48
static ILogService LogService
Log service.
Definition: ServiceRef.cs:214
Options controlling toast presentation.
Definition: ToastOptions.cs:9
bool AutoDismiss
Gets or sets whether the toast is dismissed automatically after Duration.
Definition: ToastOptions.cs:23
ToastTransition ShowTransition
Gets or sets the transition used when the toast is shown.
Definition: ToastOptions.cs:13
ToastPlacement Placement
Gets or sets the preferred placement for the toast overlay.
Definition: ToastOptions.cs:43
Task ShowAsync(View Toast, ToastOptions? Options=null)
Shows a toast view instance.
Definition: ToastService.cs:36
Task ShowAsync< TToast >(ToastOptions? Options=null)
Shows a toast view resolved from dependency injection.
Definition: ToastService.cs:29
bool HasActiveToast
Returns true if a toast is currently presented.
Definition: ToastService.cs:26
Task HideAsync()
Hides the currently active toast, if any.
Definition: ToastService.cs:46
Presenter abstraction host for page, popup and toast transitions.
Service for presenting transient toast notifications.
Definition: ImplTypes.g.cs:58
Action
The Action field indicates the action performed by the Reporting-MTA as a result of its attempt to de...