Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
EDalerUriViewModel.cs
1using System.ComponentModel;
2using System.Globalization;
3using System.Runtime.CompilerServices;
4using System.Text;
5using CommunityToolkit.Mvvm.ComponentModel;
6using CommunityToolkit.Mvvm.Input;
7using EDaler;
19using Waher.Content;
23
25{
29 public partial class EDalerUriViewModel : QrXmppViewModel
30 {
31 private readonly IAuthenticationService authenticationService = ServiceRef.Provider.GetRequiredService<IAuthenticationService>();
32
33 private readonly EDalerUriNavigationArgs? navigationArguments;
34 private readonly IShareQrCode? shareQrCode;
35 private readonly TaskCompletionSource<string?>? uriToSend = null;
36 private readonly TaskCompletionSource<string?>? messageToSend = null;
37
38 public ObservableTask<bool> GetBalanceTask { get; } = new();
39
46 : base()
47 {
48 this.navigationArguments = Args;
49 this.shareQrCode = ShareQrCode;
50
51 this.uriToSend = Args?.UriToSend;
52 this.messageToSend = Args?.MessageToSend;
53 this.FriendlyName = Args?.FriendlyName;
54
55 if (Args?.Uri is not null)
56 {
57 this.Uri = Args.Uri.UriString;
58 this.Id = Args.Uri.Id;
59 this.Amount = Args.Uri.Amount;
60 this.AmountExtra = Args.Uri.AmountExtra;
61 this.Currency = Args.Uri.Currency;
62 this.Created = Args.Uri.Created;
63 this.Expires = Args.Uri.Expires;
64 this.ExpiresStr = this.Expires.ToShortDateString();
65 this.From = Args.Uri.From;
66 this.FromType = Args.Uri.FromType;
67 this.To = Args.Uri.To;
68 this.ToType = Args.Uri.ToType;
69 this.ToPreset = !string.IsNullOrEmpty(Args.Uri.To);
70 this.Complete = Args.Uri.Complete;
71 }
72
73 this.NotPaid = true;
74
75 this.AmountText = !this.Amount.HasValue || this.Amount.Value <= 0 ? string.Empty : MoneyToString.ToString(this.Amount.Value);
76 this.AmountOk = CommonTypes.TryParse(this.AmountText, out decimal D) && D > 0;
77 this.AmountPreset = !string.IsNullOrEmpty(this.AmountText) && this.AmountOk;
78 this.AmountAndCurrency = this.AmountText + " " + this.Currency;
79
80 this.AmountExtraText = this.AmountExtra.HasValue ? MoneyToString.ToString(this.AmountExtra.Value) : string.Empty;
81 this.AmountExtraOk = string.IsNullOrEmpty(this.AmountExtraText) || CommonTypes.TryParse(this.AmountExtraText, out decimal D1) && D1 >= 0;
82 this.AmountExtraPreset = this.AmountExtra.HasValue;
83 this.AmountExtraAndCurrency = this.AmountExtraText + " " + this.Currency;
84
85 StringBuilder Url = new();
86
87 Url.Append("https://");
88 Url.Append(this.From);
89 Url.Append("/Images/eDalerFront200.png");
90
91 this.EDalerFrontGlyph = Url.ToString();
92
93 Url.Clear();
94 Url.Append("https://");
95 Url.Append(ServiceRef.TagProfile.Domain);
96 Url.Append("/Images/eDalerBack200.png");
97 this.EDalerBackGlyph = Url.ToString();
98 }
99
101 public override async Task OnInitializeAsync()
102 {
103 await base.OnInitializeAsync();
104
105 // Subscribe to petitioned identity responses (only once)
106 ServiceRef.XmppService.PetitionedIdentityResponseReceived += this.XmppService_PetitionedIdentityResponseReceived;
107
108 if (this.navigationArguments is not null)
109 {
110 if (this.navigationArguments.Uri?.EncryptedMessage is not null)
111 {
112 if (this.navigationArguments.Uri.EncryptionPublicKey is null)
113 this.Message = Encoding.UTF8.GetString(this.navigationArguments.Uri.EncryptedMessage);
114 else
115 {
116 //TODO: Fix LocalIsRecipient argument
117 bool LocalIsRecipient = this.navigationArguments.Uri.ToType == EntityType.LegalId &&
118 this.navigationArguments.Uri.To == ServiceRef.TagProfile?.LegalIdentity?.Id;
119
120 this.Message = await ServiceRef.XmppService.TryDecryptMessage(this.navigationArguments.Uri.EncryptedMessage,
121 this.navigationArguments.Uri.EncryptionPublicKey, this.navigationArguments.Uri.Id, this.navigationArguments.Uri.From, LocalIsRecipient);
122 }
123 this.HasMessage = !string.IsNullOrEmpty(this.Message);
124 }
125
126 this.MessagePreset = !string.IsNullOrEmpty(this.Message);
127 this.CanEncryptMessage = false;//this.navigationArguments.Uri?.ToType == EntityType.LegalId;
128 this.EncryptMessage = this.CanEncryptMessage;
129 }
130
131 this.GetBalanceTask.Load(this.LoadBalanceAsync);
132 }
133
135 public override async Task OnDisposeAsync()
136 {
137 this.uriToSend?.TrySetResult(null);
138 this.messageToSend?.TrySetResult(null);
139
140 ServiceRef.XmppService.PetitionedIdentityResponseReceived -= this.XmppService_PetitionedIdentityResponseReceived;
141
142 await base.OnDisposeAsync();
143 }
144
145 private async Task XmppService_PetitionedIdentityResponseReceived(object? Sender, LegalIdentityPetitionResponseEventArgs e)
146 {
147 try
148 {
149 // If we have petitioned this identity and response is positive, capture JID
150 if (e.Response && e.RequestedIdentity is not null && this.ToType == EntityType.LegalId &&
151 string.Equals(this.To, e.RequestedIdentity.Id, StringComparison.OrdinalIgnoreCase))
152 {
153 string Jid = e.RequestedIdentity.GetJid();
154 if (!string.IsNullOrEmpty(Jid))
155 {
156 MainThread.BeginInvokeOnMainThread(async () =>
157 {
158 this.To = Jid;
159 this.ToType = EntityType.NetworkJid;
160
161 // Enrich contact info if possible
162 ContactInfo? InfoByJid = await ContactInfo.FindByBareJid(Jid);
163 if (InfoByJid is not null)
164 {
165 this.ToContact = new ContactInfoModel(InfoByJid);
166 this.ContactSelected = true;
167 }
168 });
169 }
170 }
171 }
172 catch (Exception Ex)
173 {
174 ServiceRef.LogService.LogException(Ex);
175 }
176 }
177
178 #region Properties
179
183 [ObservableProperty]
184 private string? uri;
185
189 [ObservableProperty]
190 private decimal? amount;
191
195 [ObservableProperty]
196 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
197 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
198 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
199 private bool amountOk;
200
201 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
202 {
203 base.OnPropertyChanged(e);
204
205 switch (e.PropertyName)
206 {
207 case nameof(this.IsConnected):
208 this.AcceptCommand.NotifyCanExecuteChanged();
209 this.PayOnlineCommand.NotifyCanExecuteChanged();
210 this.SubmitCommand.NotifyCanExecuteChanged();
211 break;
212
213 case nameof(this.HasQrCode):
214 this.PayOnlineCommand.NotifyCanExecuteChanged();
215 this.GenerateQrCodeCommand.NotifyCanExecuteChanged();
216 this.ShareCommand.NotifyCanExecuteChanged();
217 break;
218
219 case nameof(this.AmountText):
220 if (CommonTypes.TryParse(this.AmountText, out decimal D2) && D2 > 0)
221 {
222 this.Amount = D2;
223 this.AmountOk = true;
224 }
225 else
226 this.AmountOk = false;
227
228 this.AmountAndCurrency = this.AmountText + " " + this.Currency;
229 break;
230
231 case nameof(this.AmountExtraText):
232 if (string.IsNullOrEmpty(this.AmountExtraText))
233 {
234 this.AmountExtra = null;
235 this.AmountExtraOk = true;
236 }
237 else if (CommonTypes.TryParse(this.AmountExtraText, out decimal D3) && D3 >= 0)
238 {
239 this.AmountExtra = D3;
240 this.AmountExtraOk = true;
241 }
242 else
243 this.AmountExtraOk = false;
244
245 this.AmountExtraAndCurrency = this.AmountExtraText + " " + this.Currency;
246 break;
247 }
248 }
249
253 [ObservableProperty]
254 private DateTime? balanceUpdated = ServiceRef.TagProfile.LastEDalerBalanceUpdate;
255
256
260 public decimal BalanceDecimal =>
261 this.FetchedBalance?.Amount ?? ServiceRef.TagProfile.LastEDalerBalanceDecimal;
262
263 public string BalanceString =>
264 this.BalanceDecimal + " " + this.Currency;
265
266 public decimal ReservedDecimal =>
267 this.FetchedBalance?.Reserved ?? -1;
268
269 public string ReservedString
270 {
271 get
272 {
273 if (this.ReservedDecimal == -1)
275 else
276 return this.ReservedDecimal + " " + this.Currency;
277 }
278 }
279
280 public bool HasReserved => (this.ReservedDecimal > 0 || this.ReservedDecimal == -1) && this.GetBalanceTask.IsSucceeded;
281
285 [ObservableProperty]
286 [NotifyPropertyChangedFor(nameof(BalanceDecimal))]
287 [NotifyPropertyChangedFor(nameof(BalanceString))]
288 [NotifyPropertyChangedFor(nameof(ReservedDecimal))]
289 [NotifyPropertyChangedFor(nameof(ReservedString))]
290 [NotifyPropertyChangedFor(nameof(HasReserved))]
291 Balance? fetchedBalance;
292
296 [ObservableProperty]
297 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
298 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
299 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
300 private string? amountText;
301
305 [ObservableProperty]
306 private string? amountAndCurrency;
307
311 [ObservableProperty]
312 private bool amountPreset;
313
317 [ObservableProperty]
318 private decimal? amountExtra;
319
323 [ObservableProperty]
324 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
325 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
326 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
327 private bool amountExtraOk;
328
332 [ObservableProperty]
333 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
334 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
335 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
336 private string? amountExtraText;
337
341 [ObservableProperty]
342 private string? amountExtraAndCurrency;
343
347 [ObservableProperty]
348 private bool amountExtraPreset;
349
353 [ObservableProperty]
354 private string? currency;
355
359 [ObservableProperty]
360 private DateTime created;
361
365 [ObservableProperty]
366 private DateTime expires;
367
371 [ObservableProperty]
372 private string? expiresStr;
373
377 [ObservableProperty]
378 private Guid id;
379
383 [ObservableProperty]
384 private string? from;
385
389 [ObservableProperty]
390 private EntityType fromType;
391
395 [ObservableProperty]
396 private string? to;
397
401 [ObservableProperty]
402 private ContactInfoModel? toContact;
403
407 [ObservableProperty]
408 private bool contactSelected = false;
409
413 [ObservableProperty]
414 private bool toPreset;
415
419 [ObservableProperty]
420 private EntityType toType;
421
425 [ObservableProperty]
426 private string? friendlyName;
427
431 [ObservableProperty]
432 private bool complete;
433
437 [ObservableProperty]
438 private string? message;
439
443 [ObservableProperty]
444 private bool encryptMessage;
445
449 [ObservableProperty]
450 private bool canEncryptMessage;
451
455 [ObservableProperty]
456 private bool hasMessage;
457
461 [ObservableProperty]
462 private bool messagePreset;
463
467 [ObservableProperty]
468 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
469 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
470 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
471 private bool notPaid;
472
476 [ObservableProperty]
477 private string? eDalerFrontGlyph;
478
482 [ObservableProperty]
483 private string? eDalerBackGlyph;
484
485 #endregion
486
490 [RelayCommand]
491 private async Task FromClick()
492 {
493 try
494 {
495 string? Value = this.From;
496 if (Value is null)
497 return;
498
499 if ((Value.StartsWith("http://", StringComparison.CurrentCultureIgnoreCase) ||
500 Value.StartsWith("https://", StringComparison.CurrentCultureIgnoreCase)) &&
501 System.Uri.TryCreate(Value, UriKind.Absolute, out Uri? Uri) && await Launcher.TryOpenAsync(Uri))
502 {
503 return;
504 }
505
506 if (System.Uri.TryCreate("https://" + Value, UriKind.Absolute, out Uri) && await Launcher.TryOpenAsync(Uri))
507 return;
508
509 await Clipboard.SetTextAsync(Value);
512 }
513 catch (Exception Ex)
514 {
515 ServiceRef.LogService.LogException(Ex);
517 }
518 }
519
523 [RelayCommand(CanExecute = nameof(IsConnected))]
524 private async Task Accept()
525 {
526 try
527 {
528 if (this.Uri is null)
529 return;
530
531 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.AcceptEDalerUri, true))
532 return;
533
534 Transaction? Transaction = await ServiceRef.XmppService.SendEDalerUri(this.Uri);
535
536 await this.GoBack();
539
540 }
541 catch (Exception Ex)
542 {
543 ServiceRef.LogService.LogException(Ex);
545 }
546 }
547
551 [RelayCommand(CanExecute = nameof(IsConnected))]
552 private async Task Decline()
553 {
554 await this.GoBack();
555 }
556
560 [RelayCommand(CanExecute = nameof(CanPayOnline))]
561 private async Task PayOnline()
562 {
563 try
564 {
565 if (!this.NotPaid)
566 {
569 return;
570 }
571
572 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.PayOnline, true))
573 return;
574
575 string Uri;
576
577 if (this.EncryptMessage && this.ToType == EntityType.LegalId)
578 {
579 try
580 {
581 LegalIdentity LegalIdentity = await ServiceRef.XmppService.GetLegalIdentity(this.To);
582 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(LegalIdentity, this.Amount ?? 0, this.AmountExtra,
583 this.Currency ?? string.Empty, 3, this.Message ?? string.Empty);
584 }
585 catch (ForbiddenException)
586 {
587 // This happens if you try to view someone else's legal identity.
588 // When this happens, try to send a petition to view it instead.
589 // Normal operation. Should not be logged.
590
591 this.NotPaid = true;
592
593 MainThread.BeginInvokeOnMainThread(async () =>
594 {
595 bool Succeeded = await ServiceRef.NetworkService.TryRequest(() => ServiceRef.XmppService.PetitionIdentity(
596 this.To, Guid.NewGuid().ToString(), ServiceRef.Localizer[nameof(AppResources.EncryptedPayment)]));
597
598 if (Succeeded)
599 {
602 }
603 });
604
605 return;
606 }
607 }
608 else
609 {
610 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(this.To!, this.Amount ?? 0, this.AmountExtra == 0 ? null : this.AmountExtra,
611 this.Currency!, 3, this.Message ?? string.Empty);
612 }
613
614 // TODO: Validate To is a Bare JID or proper Legal Identity
615 // TODO: Offline options: Expiry days
616
617 this.NotPaid = false;
618
619 (bool Succeeded, Transaction? Transaction) = await ServiceRef.NetworkService.TryRequest(
620 () => ServiceRef.XmppService.SendEDalerUri(Uri));
621
622 if (Succeeded)
623 {
624 await this.GoBack();
625 PaymentSuccessPopup Popup = new(Transaction!, this.Message);
626 await ServiceRef.PopupService.PushAsync(Popup);
627 }
628 else
629 {
630 this.NotPaid = true;
633 }
634 }
635 catch (Exception Ex)
636 {
637 this.NotPaid = true;
638 ServiceRef.LogService.LogException(Ex);
640 }
641 }
642
646 [RelayCommand(CanExecute = nameof(CanGenerateQrCode))]
647 private async Task GenerateQrCode()
648 {
649 if (!this.NotPaid)
650 {
653 return;
654 }
655
656 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.PayOffline, true))
657 return;
658
659 try
660 {
661 string Uri;
662 if (this.EncryptMessage && this.ToType == EntityType.LegalId)
663 {
664 LegalIdentity LegalIdentity = await ServiceRef.XmppService.GetLegalIdentity(this.To);
665 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(LegalIdentity, this.Amount ?? 0, this.AmountExtra,
666 this.Currency ?? string.Empty, 3, this.Message ?? string.Empty);
667 }
668 else
669 {
670 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(this.To!, this.Amount ?? 0, this.AmountExtra,
671 this.Currency ?? string.Empty, 3, this.Message ?? string.Empty);
672 }
673
674 // TODO: Validate To is a Bare JID or proper Legal Identity
675 // TODO: Offline options: Expiry days
676
677 if (this.IsAppearing)
678 {
679 MainThread.BeginInvokeOnMainThread(async () =>
680 {
681 this.GenerateQrCode(Uri);
682
683 if (this.shareQrCode is not null)
684 await this.shareQrCode.ShowQrCode();
685 });
686 }
687 }
688 catch (Exception Ex)
689 {
690 ServiceRef.LogService.LogException(Ex);
692 }
693 }
694
695 private bool CanPayOnline => this.AmountOk && this.AmountExtraOk && !this.HasQrCode && this.IsConnected && this.NotPaid; // TODO: Add To field OK
696 private bool CanGenerateQrCode => this.AmountOk && this.AmountExtraOk && !this.HasQrCode && this.NotPaid; // TODO: Add To field OK
697 private bool CanShare => this.HasQrCode;
698
702 [RelayCommand(CanExecute = nameof(CanShare))]
703 private async Task Share()
704 {
705 if (this.QrCodeBin is null)
706 return;
707
708 try
709 {
710 string? Message = this.Message ?? this.AmountAndCurrency;
711
713 string.Format(CultureInfo.CurrentCulture, Message ?? string.Empty, this.Amount, this.Currency),
714 ServiceRef.Localizer[nameof(AppResources.Share)], "RequestPayment.png");
715 }
716 catch (Exception Ex)
717 {
718 ServiceRef.LogService.LogException(Ex);
720 }
721 }
722
726 [RelayCommand(CanExecute = nameof(IsConnected))]
727 private async Task Submit()
728 {
729 if (this.Uri is null)
730 return;
731
732 try
733 {
734 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.SubmitEDalerUri))
735 return;
736
737 (bool Succeeded, Transaction? Transaction) = await ServiceRef.NetworkService.TryRequest(() => ServiceRef.XmppService.SendEDalerUri(this.Uri));
738 if (Succeeded)
739 {
740 await this.GoBack();
741 PaymentSuccessPopup Popup = new(Transaction!, this.Message);
742 await ServiceRef.PopupService.PushAsync(Popup);
743 }
744 else
747 }
748 catch (Exception Ex)
749 {
750 ServiceRef.LogService.LogException(Ex);
752 }
753 }
754
758 [RelayCommand]
759 private async Task ShowCode()
760 {
761 if (this.Uri is null)
762 return;
763
764 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.ShowUriAsQr, true))
765 return;
766
767 try
768 {
769 if (this.IsAppearing)
770 {
771 MainThread.BeginInvokeOnMainThread(async () =>
772 {
773 this.GenerateQrCode(this.Uri);
774
775 if (this.shareQrCode is not null)
776 await this.shareQrCode.ShowQrCode();
777 });
778 }
779 }
780 catch (Exception Ex)
781 {
782 ServiceRef.LogService.LogException(Ex);
784 }
785 }
786
787 private bool CanSendPayment()
788 {
789 return this.uriToSend is not null && this.AmountOk && this.AmountExtraOk && this.NotPaid;
790 }
791
795 [RelayCommand(CanExecute = nameof(CanSendPayment))]
796 private async Task SendPayment()
797 {
798 if (!this.NotPaid)
799 {
802 return;
803 }
804
805 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.SendPayment, true))
806 return;
807
808 try
809 {
810 string Uri;
811
812 if (this.EncryptMessage && this.ToType == EntityType.LegalId)
813 {
814 LegalIdentity LegalIdentity = await ServiceRef.XmppService.GetLegalIdentity(this.To);
815 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(LegalIdentity, this.Amount ?? 0, this.AmountExtra,
816 this.Currency!, 3, this.Message ?? string.Empty);
817 }
818 else if(this.ToType == EntityType.LegalId)
819 {
820 //TODO: Verify that JID is available
821 LegalIdentity LegalIdentity = await ServiceRef.XmppService.GetLegalIdentity(this.To!);
822 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(LegalIdentity.GetJid(), this.Amount ?? 0, this.AmountExtra,
823 this.Currency!, 3, this.Message ?? string.Empty);
824 }
825 else
826 {
827 Uri = await ServiceRef.XmppService.CreateFullEDalerPaymentUri(this.To!, this.Amount ?? 0, this.AmountExtra,
828 this.Currency!, 3, this.Message ?? string.Empty);
829 }
830
831 // TODO: Validate To is a Bare JID or proper Legal Identity
832 // TODO: Offline options: Expiry days
833
834 this.uriToSend?.TrySetResult(Uri);
835 this.messageToSend?.TrySetResult(this.Message);
836 await this.GoBack();
837 }
838 catch (Exception Ex)
839 {
840 ServiceRef.LogService.LogException(Ex);
842 }
843 }
844
849 [RelayCommand]
850 public async Task OpenCalculator(object Parameter)
851 {
852 try
853 {
854 switch (Parameter?.ToString())
855 {
856 case "AmountText":
857 CalculatorNavigationArgs AmountArgs = new(this, nameof(this.AmountText));
858
859 await ServiceRef.NavigationService.GoToAsync(nameof(CalculatorPage), AmountArgs, BackMethod.Pop);
860 break;
861
862 case "AmountExtraText":
863 CalculatorNavigationArgs ExtraArgs = new(this, nameof(this.AmountExtraText));
864
865 await ServiceRef.NavigationService.GoToAsync(nameof(CalculatorPage), ExtraArgs, BackMethod.Pop);
866 break;
867 }
868 }
869 catch (Exception Ex)
870 {
872 }
873 }
874
875 #region ILinkableView
876
880 public override Task<string> Title => Task.FromResult<string>(ServiceRef.Localizer[nameof(AppResources.Payment)]);
881
882 #endregion
883
884 [RelayCommand]
885 private async Task SelectRecipient()
886 {
887 try
888 {
889 TaskCompletionSource<ContactInfoModel?> Selected = new();
891 ContactListNavigationArgs ContactListArgs = new(Description, Selected)
892 {
893 CanScanQrCode = true
894 };
895
896 await ServiceRef.NavigationService.GoToAsync(nameof(MyContactsPage), ContactListArgs, BackMethod.Pop);
897
898 ContactInfoModel? Contact = await Selected.Task;
899 if (Contact is null)
900 return;
901
902 this.ToContact = Contact;
903 this.ContactSelected = true;
904
905 if (!string.IsNullOrEmpty(Contact.LegalId))
906 {
907 this.To = Contact.LegalId;
908 this.ToType = EntityType.LegalId;
909 }
910 else if (!string.IsNullOrEmpty(Contact.BareJid))
911 {
912 this.To = Contact.BareJid;
913 this.ToType = EntityType.NetworkJid; // Using Network to represent a bare JID
914 }
915 else
916 {
919 return;
920 }
921
922 this.ToPreset = false;
923 }
924 catch (Exception Ex)
925 {
926 ServiceRef.LogService.LogException(Ex);
928 }
929 }
930
931 [RelayCommand]
932 private Task ClearRecipient()
933 {
934 this.To = string.Empty;
935 this.ToContact = null;
936 this.ContactSelected = false;
937
938 return Task.CompletedTask;
939 }
940
945 private async Task LoadBalanceAsync(TaskContext<bool> Ctx)
946 {
947 ServiceRef.LogService.LogDebug("Refreshing Edaler...");
948
949 if (!await ServiceRef.XmppService.WaitForConnectedState(Constants.Timeouts.XmppConnect))
950 return;
951
952 Balance CurrentBalance = await ServiceRef.XmppService.GetEDalerBalance();
953
954 MainThread.BeginInvokeOnMainThread(() =>
955 {
956 this.FetchedBalance = CurrentBalance;
957 this.BalanceUpdated = DateTime.UtcNow;
958 ServiceRef.TagProfile.LastEDalerBalanceDecimal = this.BalanceDecimal;
959 ServiceRef.TagProfile.LastEDalerBalanceUpdate = DateTime.UtcNow;
960 });
961
962 ServiceRef.LogService.LogDebug("Refreshing Edaler Completed");
963 }
964
965 [RelayCommand]
966 private async Task ScanQr()
967 {
968 try
969 {
970 string[] AllowedSchemas = [Constants.UriSchemes.IotId];
971 string? Url = await Services.UI.QR.QrCode.ScanQrCode(nameof(AppResources.ScanQRCode), AllowedSchemas);
972 if (string.IsNullOrEmpty(Url))
973 return;
974
975 string? NeuroId = null;
976
977 // Accept formats:
978 // 1. iotid:LegalIdentityId
979 // 2. Raw LegalIdentityId (no scheme)
980 if (Url.StartsWith(Constants.UriSchemes.IotId + ":", StringComparison.OrdinalIgnoreCase))
981 {
982 int i = Url.IndexOf(':');
983 NeuroId = Url[(i + 1)..].Trim();
984 }
985 else
986 NeuroId = Url.Trim();
987
988 if (string.IsNullOrEmpty(NeuroId))
989 {
992 return;
993 }
994
995 // Set recipient as Legal ID initially
996 this.To = NeuroId;
997 this.ToType = EntityType.LegalId;
998 this.ToPreset = false;
999 this.ToContact = null;
1000 this.ContactSelected = false;
1001
1002 // Petition identity to obtain JID (asynchronous response via event)
1003 try
1004 {
1005 await ServiceRef.NetworkService.TryRequest(() =>
1006 ServiceRef.XmppService.PetitionIdentity(NeuroId, Guid.NewGuid().ToString(), ServiceRef.Localizer[nameof(AppResources.Payment)])
1007 );
1008 }
1009 catch (Exception Ex2)
1010 {
1011 ServiceRef.LogService.LogException(Ex2);
1014 }
1015 }
1016 catch (Exception Ex)
1017 {
1018 ServiceRef.LogService.LogException(Ex);
1020 }
1021 }
1022
1023 }
1024}
Contains information about a balance.
Definition: Balance.cs:11
Represents a transaction in the eDaler network.
Definition: Transaction.cs:36
static readonly TimeSpan XmppConnect
XMPP Connect timeout
Definition: Constants.cs:702
const string IotId
The IoT ID URI Scheme (iotid)
Definition: Constants.cs:153
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 APetitionHasBeenSentForEncryption
Looks up a localized string similar to Encryption can only be performed, if you have access to the re...
static string CodeNotRecognized
Looks up a localized string similar to Code not recognized..
static string PaymentAlreadySent
Looks up a localized string similar to Payment instruction has already been sent. You can resend pend...
static string UnableToOpenLink
Looks up a localized string similar to Unable to open link:.
static string UnableToProcessEDalerUri
Looks up a localized string similar to Unable to process eDaler code..
static string TransactionAccepted
Looks up a localized string similar to Transaction has been accepted and processed....
static string ScanQRCode
Looks up a localized string similar to Scan QR Code.
static string UnknownPleaseRefresh
Looks up a localized string similar to Unknown, please refresh.
static string Payment
Looks up a localized string similar to Payment.
static string Share
Looks up a localized string similar to Share.
static string TagValueCopiedToClipboard
Looks up a localized string similar to Tag value copied to clipboard.
static string NetworkAddressOfContactUnknown
Looks up a localized string similar to Network address of contact unknown..
static string PetitionSent
Looks up a localized string similar to Petition sent.
static string EncryptedPayment
Looks up a localized string similar to Encrypted Payment.
static string SelectFromWhomToRequestPayment
Looks up a localized string similar to Select from whom to request payment..
static string SuccessTitle
Looks up a localized string similar to Success.
static string ErrorTitle
Looks up a localized string similar to An error has occurred.
Contains information about a contact.
Definition: ContactInfo.cs:22
static Task< ContactInfo > FindByBareJid(string BareJid)
Finds information about a contact, given its Bare JID.
Definition: ContactInfo.cs:221
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
static INetworkService NetworkService
Network service.
Definition: ServiceRef.cs:226
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 IPopupService PopupService
Popup service for presenting application popups.
Definition: ServiceRef.cs:142
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 IPlatformSpecific PlatformSpecific
Localization service
Definition: ServiceRef.cs:383
static string ToString(decimal Money)
Converts a monetary value to a string, removing any round-off errors.
Provides a data-binding friendly mechanism to manage and report the status of asynchronous operations...
bool IsAppearing
Returns true if the view model is shown.
virtual async Task GoBack()
Method called when user wants to navigate to the previous screen.
Contact Information model, including related notification information.
CaseInsensitiveString? LegalId
Legal ID of contact.
CaseInsensitiveString? BareJid
Bare JID of contact.
Holds navigation parameters specific to views displaying a list of contacts.
A page that displays a list of the current user's contacts.
A page that allows the user to calculate the value of a numerical input field.
A view model that holds the XMPP state.
Holds navigation parameters specific to eDaler URIs.
string? FriendlyName
Optional Friendly Name associated with URI
TaskCompletionSource< string?>? UriToSend
Task Completion Source in case the URI being built is to be returned to the parent page.
TaskCompletionSource< string?>? MessageToSend
Task Completion Source in case a message being sent is to be returned to the parent page.
The view model to bind to for when displaying the contents of an eDaler URI.
async Task OpenCalculator(object Parameter)
Opens the calculator for calculating the value of a numerical property.
decimal BalanceDecimal
Exposes the current balance as a decimal.
EDalerUriViewModel(IShareQrCode? ShareQrCode, EDalerUriNavigationArgs? Args)
The view model to bind to for when displaying the contents of an eDaler URI.
override Task< string > Title
Title of the current view
override async Task OnInitializeAsync()
Method called when view is initialized for the first time. Use this method to implement registration ...
override async Task OnDisposeAsync()
Method called when the view is disposed, and will not be used more. Use this method to unregister eve...
Helps with parsing of commong data types.
Definition: CommonTypes.cs:15
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
Abstract base class for contractual parameters
Definition: Parameter.cs:17
The requesting entity does not possess the necessary permissions to perform an action that only certa...
void ShareImage(byte[] PngFile, string Message, string Title, string FileName)
Shares an image in PNG format.
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.
Task< bool > DisplayAlert(string Title, string Message, string? Accept=null, string? Cancel=null)
Displays an alert/message box to the user.
Interface for pages with a share button.
Definition: IShareQrCode.cs:7
EntityType
Type of entity referred to in transaction.
Definition: Transaction.cs:15
BackMethod
Navigation Back Method
Definition: BackMethod.cs:7
AuthenticationPurpose
Purpose for requesting the user to authenticate itself.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.