Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
HomeViewModel.cs
1using System.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
8using CommunityToolkit.Mvvm.ComponentModel;
12using EDaler;
16using System.Globalization;
19using NeuroAccessMaui.Services.Data; // Added for Database access
20using System.Linq;
25using NeuroAccessMaui.Services.Tag; // Added for ordering
29using Microsoft.Extensions.DependencyInjection;
31using System.Threading;
33
35{
36 public partial class HomeViewModel : QrXmppViewModel
37 {
38 private readonly IAuthenticationService authenticationService = ServiceRef.AuthenticationService;
39 private readonly INotificationServiceV2 notificationService;
40
41 public string BannerUriLight => ServiceRef.ThemeService.GetImageUri(Constants.Branding.BannerLargeLight);
42 public string BannerUriDark => ServiceRef.ThemeService.GetImageUri(Constants.Branding.BannerLargeDark);
43
44 [ObservableProperty]
45 bool themeLoaded = false;
46
47 private IdentityState? latestCreatedIdentityState; // Cached state from latest stored KYC reference
48 private ApplicationReview? latestApplicationReview;
49 private bool reviewEventSubscribed;
50
51 public string BannerUri =>
52 Application.Current?.UserAppTheme switch
53 {
54 AppTheme.Dark => this.BannerUriDark,
55 AppTheme.Light => this.BannerUriLight,
56 _ => this.BannerUriLight
57 } ?? this.BannerUriLight;
58
59 public HomeViewModel()
60 : base()
61 {
62 this.notificationService = ServiceRef.Provider.GetRequiredService<INotificationServiceV2>();
63
64 Application.Current.RequestedThemeChanged += (_, __) =>
65 OnPropertyChanged(nameof(BannerUri));
66 }
67
68 public override Task<string> Title => Task.FromResult(ContactInfo.GetFriendlyName(ServiceRef.TagProfile.LegalIdentity));
69
70 public override async Task OnAppearingAsync()
71 {
72 await base.OnAppearingAsync();
73
74 try
75 {
76 // Load latest stored KYC reference state
77 await this.LoadLatestKycStateAsync();
78
79 try
80 {
82 await ServiceRef.PermissionService.CheckNotificationPermissionAsync();
83
85 }
86 catch
87 {
88 //Normal operation if Notification is not supported or denied
89 }
90
91 _ = await ServiceRef.XmppService.WaitForConnectedState(Constants.Timeouts.XmppConnect);
92 await ServiceRef.ThemeService.ThemeLoaded.Task;
93 MainThread.BeginInvokeOnMainThread(() =>
94 {
95 this.ThemeLoaded = true;
96 this.OnPropertyChanged(nameof(this.BannerUri));
97 });
98 await ServiceRef.IntentService.ProcessQueuedIntentsAsync();
99
100
101 // GeoMapViewModel vm = new(59.638346832492765,11.879682074310969);
102 // await ServiceRef.PopupService.PushAsync(new GeoMapPopup(vm));
103 // Console.WriteLine($"GeoMap result: {await vm.Result}");
104
105 }
106 catch (Exception Ex)
107 {
108 ServiceRef.LogService.LogException(Ex);
109 }
110
111 ServiceRef.XmppService.IdentityApplicationChanged += this.XmppService_IdentityApplicationChanged;
112 ServiceRef.XmppService.LegalIdentityChanged += this.XmppService_LegalIdentityChanged;
113 ServiceRef.TagProfile.OnPropertiesChanged += this.TagProfile_OnPropertiesChanged;
114 ServiceRef.TagProfile.Changed += this.TagProfile_PropertyChanged;
115 if (!this.reviewEventSubscribed)
116 {
117 ServiceRef.KycService.ApplicationReviewUpdated += this.KycService_ApplicationReviewUpdated;
118 this.reviewEventSubscribed = true;
119 }
120
121 this.notificationService.OnNotificationAdded += this.NotificationService_OnNotificationAdded;
122 await this.RefreshUnreadNotificationsAsync();
123 }
124
125 public override Task OnDisappearingAsync()
126 {
127 ServiceRef.XmppService.IdentityApplicationChanged -= this.XmppService_IdentityApplicationChanged;
128 ServiceRef.XmppService.LegalIdentityChanged -= this.XmppService_LegalIdentityChanged;
129 ServiceRef.TagProfile.OnPropertiesChanged -= this.TagProfile_OnPropertiesChanged;
130 ServiceRef.TagProfile.Changed -= this.TagProfile_PropertyChanged;
131 if (this.reviewEventSubscribed)
132 {
133 ServiceRef.KycService.ApplicationReviewUpdated -= this.KycService_ApplicationReviewUpdated;
134 this.reviewEventSubscribed = false;
135 }
136 this.notificationService.OnNotificationAdded -= this.NotificationService_OnNotificationAdded;
137 return base.OnDisappearingAsync();
138 }
139
140 private void TagProfile_OnPropertiesChanged(object? Sender, EventArgs e)
141 {
142 Task.Run(this.LoadLatestKycStateAsync);
143 }
144
145 private void TagProfile_PropertyChanged(object? sender, PropertyChangedEventArgs e)
146 {
147 if (string.IsNullOrEmpty(e.PropertyName) ||
148 e.PropertyName == nameof(ITagProfile.IdentityApplication) ||
149 e.PropertyName == nameof(ITagProfile.LegalIdentity))
150 {
151 MainThread.BeginInvokeOnMainThread(() =>
152 {
153 this.OnPropertyChanged(nameof(this.HasPersonalIdentity));
154 this.OnPropertyChanged(nameof(this.HasPendingIdentity));
155 this.OnPropertyChanged(nameof(this.ShowApplyIdBox));
156 this.OnPropertyChanged(nameof(this.ShowPendingIdBox));
157 this.OnPropertyChanged(nameof(this.ShowRejectedIdBox));
158 this.OnPropertyChanged(nameof(this.ShowInfoBubble));
159 this.OnPropertyChanged(nameof(this.ShowIdButtonText));
160 });
161 }
162 }
163
164 private async Task XmppService_IdentityApplicationChanged(object? Sender, EventArgs e)
165 {
166 // Refresh cached KYC state when identity application changes
167 await this.LoadLatestKycStateAsync();
168 }
169
170 private async Task XmppService_LegalIdentityChanged(object? Sender, EventArgs e)
171 {
172 await this.LoadLatestKycStateAsync();
173 }
174
175 public override async Task OnInitializeAsync()
176 {
177 await base.OnInitializeAsync();
178
179 await this.OnIsConnectedChanged(); // Call this method in case the connection state has already changed before the view model was initialized.
180 }
181
182 protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
183 {
184 base.OnPropertyChanged(e);
185
186 switch (e.PropertyName)
187 {
188 case nameof(this.IsConnected):
189 await this.OnIsConnectedChanged();
190 break;
191 }
192 }
193
194 private async Task OnIsConnectedChanged()
195 {
196 try
197 {
198 if (this.IsConnected && ServiceRef.TagProfile.LegalIdentityNeedsRefreshing())
199 {
200 LegalIdentity RefreshedIdentity = await ServiceRef.XmppService.GetLegalIdentity(ServiceRef.TagProfile.LegalIdentity?.Id);
201 await MainThread.InvokeOnMainThreadAsync(async () => await ServiceRef.TagProfile.SetLegalIdentity(RefreshedIdentity, false));
202 }
203 }
204 catch (Exception Ex)
205 {
206 ServiceRef.LogService.LogException(Ex);
207 }
208 finally
209 {
210 this.ScanQrCodeCommand.NotifyCanExecuteChanged();
211 }
212 }
213
214 public string ShowIdButtonText => this.HasPersonalIdentity ? ServiceRef.Localizer[nameof(AppResources.ShowIDShort)] : ServiceRef.Localizer[nameof(AppResources.ShowAccount)];
215
216 public bool HasPersonalIdentity => ServiceRef.TagProfile.LegalIdentity?.HasApprovedPersonalInformation() ?? false;
217 public bool HasPendingIdentity => this.CheckPendingIdentity();
218
219 public bool ShowInfoBubble => this.ShowApplyIdBox || this.ShowPendingIdBox || this.ShowRejectedIdBox;
220 public bool ShowApplyIdBox => !(ServiceRef.TagProfile.LegalIdentity?.HasApprovedPersonalInformation() ?? false) && !this.CheckPendingIdentity() && !this.CheckRejectedIdentity();
221 // Only show pending while the application is in Created (review may be ongoing but not rejected).
222 public bool ShowPendingIdBox => this.CheckPendingIdentity();
223 public bool ShowRejectedIdBox => this.CheckRejectedIdentity();
224
225 private bool CheckPendingIdentity()
226 {
227 IdentityState? state = this.latestCreatedIdentityState ?? ServiceRef.TagProfile.IdentityApplication?.State;
228 return state == IdentityState.Created;
229 }
230
231 private bool CheckRejectedIdentity()
232 {
233 IdentityState? state = this.latestCreatedIdentityState ?? ServiceRef.TagProfile.IdentityApplication?.State;
234 return state == IdentityState.Rejected;
235 }
236
237 private Task NotificationService_OnNotificationAdded(object? Sender, NotificationRecordEventArgs e)
238 {
239 return this.RefreshUnreadNotificationsAsync();
240 }
241
242 private async Task RefreshUnreadNotificationsAsync()
243 {
244 try
245 {
246 NotificationQuery Query = new()
247 {
248 States = new List<NotificationState>
249 {
251 NotificationState.Delivered
252 }
253 };
254
255 IReadOnlyList<NotificationRecord> Records = await this.notificationService.GetAsync(Query, CancellationToken.None);
256 MainThread.BeginInvokeOnMainThread(() =>
257 {
258 this.UnreadNotificationCount = Records.Count;
259 });
260 }
261 catch (Exception Ex)
262 {
263 ServiceRef.LogService.LogException(Ex);
264 }
265 }
266
270 public bool HasUnreadNotifications => this.UnreadNotificationCount > 0;
271
275 [ObservableProperty]
276 private int unreadNotificationCount;
277
278 partial void OnUnreadNotificationCountChanged(int value)
279 {
280 this.OnPropertyChanged(nameof(this.HasUnreadNotifications));
281 }
282
283 private async Task LoadLatestKycStateAsync()
284 {
285 try
286 {
287 KycReference? LatestReference = await FindMostRecentReferenceAsync();
288 this.latestCreatedIdentityState = LatestReference?.CreatedIdentityState;
289 this.latestApplicationReview = LatestReference?.ApplicationReview;
290
291 MainThread.BeginInvokeOnMainThread(() =>
292 {
293 this.OnPropertyChanged(nameof(this.HasPersonalIdentity));
294 this.OnPropertyChanged(nameof(this.HasPendingIdentity));
295 this.OnPropertyChanged(nameof(this.ShowPendingIdBox));
296 this.OnPropertyChanged(nameof(this.ShowApplyIdBox));
297 this.OnPropertyChanged(nameof(this.ShowRejectedIdBox));
298 this.OnPropertyChanged(nameof(this.ShowInfoBubble));
299 this.OnPropertyChanged(nameof(this.ShowIdButtonText));
300 });
301 }
302 catch (Exception Ex)
303 {
304 ServiceRef.LogService.LogException(Ex);
305 }
306 }
307
308 private void KycService_ApplicationReviewUpdated(object? sender, ApplicationReviewEventArgs e)
309 {
310 this.latestApplicationReview = e.Review;
311 MainThread.BeginInvokeOnMainThread(() =>
312 {
313 this.OnPropertyChanged(nameof(this.ShowInfoBubble));
314 this.OnPropertyChanged(nameof(this.ShowPendingIdBox));
315 this.OnPropertyChanged(nameof(this.ShowRejectedIdBox));
316 this.OnPropertyChanged(nameof(this.ShowApplyIdBox));
317 this.OnPropertyChanged(nameof(this.ShowIdButtonText));
318 });
319 }
320
321 private static async Task<KycReference?> FindMostRecentReferenceAsync()
322 {
324 if (IdentityApplication is not null)
325 {
326 try
327 {
328 KycReference? Match = await Database.FindFirstIgnoreRest<KycReference>(new FilterFieldEqualTo(nameof(KycReference.CreatedIdentityId), IdentityApplication.Id));
329 if (Match is not null)
330 return Match;
331 }
332 catch (Exception Ex)
333 {
334 ServiceRef.LogService.LogException(Ex);
335 }
336 }
337
338 try
339 {
340 IEnumerable<KycReference> All = await Database.Find<KycReference>();
341 return All.OrderByDescending(r => r.UpdatedUtc).FirstOrDefault();
342 }
343 catch (Exception Ex)
344 {
345 ServiceRef.LogService.LogException(Ex);
346 }
347
348 return null;
349 }
350
351 public bool CanScanQrCode => true;
352
353 [RelayCommand(CanExecute = nameof(CanScanQrCode))]
354 private async Task ScanQrCode()
355 {
356 await MainThread.InvokeOnMainThreadAsync(async () =>
357 {
358 await Services.UI.QR.QrCode.ScanQrCodeAndHandleResult();
359 });
360 }
361
362 [RelayCommand(AllowConcurrentExecutions = false)]
363 public async Task ViewId()
364 {
365 try
366 {
367 if (await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.ViewId))
369 }
370 catch (Exception Ex)
371 {
372 ServiceRef.LogService.LogException(Ex);
373 }
374 }
375
376 [RelayCommand(AllowConcurrentExecutions = false)]
377 public async Task OpenNotifications()
378 {
379 try
380 {
382 }
383 catch (Exception Ex)
384 {
385 ServiceRef.LogService.LogException(Ex);
386 }
387 }
388
389 [RelayCommand(AllowConcurrentExecutions = false)]
390 public async Task GoToApplyIdentity()
391 {
392 try
393 {
395 }
396 catch (Exception Ex)
397 {
398 ServiceRef.LogService.LogException(Ex);
399 }
400 }
401
402 // Go to Apps page
403 [RelayCommand]
404 public async Task ViewApps()
405 {
406 try
407 {
409 }
410 catch (Exception Ex)
411 {
412 ServiceRef.LogService.LogException(Ex);
413 }
414 }
415
416 [ObservableProperty]
417 private bool showingNoWalletPopup = false;
418
419 [RelayCommand(AllowConcurrentExecutions = false)]
420 public async Task OpenWallet()
421 {
423 {
424 await ShowWallet();
425 return;
426 }
427 else
428 {
429 this.ShowingNoWalletPopup = true;
430 await Task.Delay(5000);
431 this.ShowingNoWalletPopup = false;
432 }
433 }
434
435 [RelayCommand]
436 internal static async Task ShowWallet()
437 {
438 try
439 {
440 WalletNavigationArgs Args = new();
441
443 }
444 catch (Exception Ex)
445 {
446 ServiceRef.LogService.LogException(Ex);
448 }
449 }
450
454 [RelayCommand]
455 private static async Task ShowSettings()
456 {
457 try
458 {
460 }
461 catch (Exception Ex)
462 {
463 ServiceRef.LogService.LogException(Ex);
464 }
465 }
466 }
467}
Image identifiers for branding.
Definition: Constants.cs:1083
const string BannerLargeLight
The default branding image.
Definition: Constants.cs:1087
Runtime setting key names.
Definition: Constants.cs:1024
const string PushNotificationAsked
Push-notification asked.
Definition: Constants.cs:1048
static readonly TimeSpan XmppConnect
XMPP Connect timeout
Definition: Constants.cs:702
A set of never changing property constants and helpful values.
Definition: Constants.cs:24
A strongly-typed resource class, for looking up localized strings, etc.
static string ShowIDShort
Looks up a localized string similar to Show ID.
static string ShowAccount
Looks up a localized string similar to Show account.
Contains information about a contact.
Definition: ContactInfo.cs:22
static async Task< string > GetFriendlyName(CaseInsensitiveString RemoteId)
Gets the friendly name of a remote identity (Legal ID or Bare JID).
Definition: ContactInfo.cs:258
Captures the result of an application review returned from backend services.
Event arguments emitted when a KYC application review changes.
Contains a local reference to a KYC process.
Definition: KycReference.cs:22
ApplicationReview? ApplicationReview
Latest backend review for this application, if any.
IdentityState? CreatedIdentityState
Last known state of the created identity (if any), for quick status tagging offline.
Definition: KycReference.cs:81
string? CreatedIdentityId
The legal ID of the created identity (if any).
Definition: KycReference.cs:75
Query options for retrieving notifications.
Base class that references services in the app.
Definition: ServiceRef.cs:43
static IAuthenticationService AuthenticationService
Authentication service.
Definition: ServiceRef.cs:457
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
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:130
static INavigationService NavigationService
The navigation service for navigating between pages.
Definition: ServiceRef.cs:178
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:202
static IReportingStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:370
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:190
static IPermissionService PermissionService
Permission Service
Definition: ServiceRef.cs:358
static ISettingsService SettingsService
Settings service.
Definition: ServiceRef.cs:298
A page to display when the user wants to view an identity.
override async Task OnAppearingAsync()
Method called when view is appearing on the screen.
bool HasUnreadNotifications
Gets a value indicating whether there are unread notifications.
override async Task OnInitializeAsync()
Method called when view is initialized for the first time. Use this method to implement registration ...
override Task< string > Title
Title of the current view
override Task OnDisappearingAsync()
Method called when view is disappearing from the screen.
A view model that holds the XMPP state.
Holds navigation parameters specific to the eDaler wallet.
A page that allows the user to view the contents of its eDaler wallet, pending payments and recent ac...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
This filter selects objects that have a named field equal to a given value.
Interface for the redesigned notification service.
Task SaveState(string Key, string State)
Saves State with the given key.
Task< bool > RestoreBoolState(string Key, bool DefaultValueIfNotFound=default)
Restores State for the specified key.
The TAG Profile is the heart of the digital identity for a specific user/device. Use this instance to...
Definition: ITagProfile.cs:18
Task SetLegalIdentity(LegalIdentity? Identity, bool RemoveOldAttachments)
Sets the legal identity of the profile.
bool HasBetaFeatures
If the user has Beta features enabled
Definition: ITagProfile.cs:262
LegalIdentity? IdentityApplication
Any current Identity application.
Definition: ITagProfile.cs:227
bool LegalIdentityNeedsRefreshing()
If the current ITagProfile needs to have its legal identity refreshed, because it can have missed off...
LegalIdentity? LegalIdentity
The legal identity of the current user/profile.
Definition: ITagProfile.cs:222
Task GoToAsync(string Route)
Navigates to the specified route and pushes the page onto the navigation stack.
Task DisplayException(Exception Exception, string? Title=null)
Displays an alert/message box to the user.
Definition: ImplTypes.g.cs:58
NotificationState
Notification lifecycle state.
BackMethod
Navigation Back Method
Definition: BackMethod.cs:7
AuthenticationPurpose
Purpose for requesting the user to authenticate itself.
IdentityState
Lists recognized legal identity states.