1using System.ComponentModel;
2using System.Globalization;
3using System.Runtime.CompilerServices;
5using CommunityToolkit.Mvvm.ComponentModel;
6using CommunityToolkit.Mvvm.Input;
35 private readonly TaskCompletionSource<string?>? uriToSend =
null;
36 private readonly TaskCompletionSource<string?>? messageToSend =
null;
48 this.navigationArguments = Args;
49 this.shareQrCode = ShareQrCode;
55 if (Args?.Uri is not
null)
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;
75 this.AmountText = !this.Amount.HasValue || this.Amount.Value <= 0 ? string.Empty :
MoneyToString.
ToString(this.Amount.Value);
77 this.AmountPreset = !
string.IsNullOrEmpty(this.AmountText) && this.AmountOk;
78 this.AmountAndCurrency = this.AmountText +
" " + this.Currency;
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;
85 StringBuilder Url =
new();
87 Url.Append(
"https://");
88 Url.Append(this.From);
89 Url.Append(
"/Images/eDalerFront200.png");
91 this.EDalerFrontGlyph = Url.ToString();
94 Url.Append(
"https://");
96 Url.Append(
"/Images/eDalerBack200.png");
97 this.EDalerBackGlyph = Url.ToString();
103 await base.OnInitializeAsync();
106 ServiceRef.XmppService.PetitionedIdentityResponseReceived += this.XmppService_PetitionedIdentityResponseReceived;
108 if (this.navigationArguments is not
null)
110 if (this.navigationArguments.Uri?.EncryptedMessage is not
null)
112 if (this.navigationArguments.Uri.EncryptionPublicKey is
null)
113 this.Message = Encoding.UTF8.GetString(this.navigationArguments.Uri.EncryptedMessage);
117 bool LocalIsRecipient = this.navigationArguments.Uri.ToType == EntityType.LegalId &&
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);
123 this.HasMessage = !
string.IsNullOrEmpty(this.Message);
126 this.MessagePreset = !
string.IsNullOrEmpty(this.Message);
127 this.CanEncryptMessage =
false;
128 this.EncryptMessage = this.CanEncryptMessage;
131 this.GetBalanceTask.Load(this.LoadBalanceAsync);
137 this.uriToSend?.TrySetResult(
null);
138 this.messageToSend?.TrySetResult(
null);
140 ServiceRef.XmppService.PetitionedIdentityResponseReceived -= this.XmppService_PetitionedIdentityResponseReceived;
142 await base.OnDisposeAsync();
154 if (!
string.IsNullOrEmpty(Jid))
156 MainThread.BeginInvokeOnMainThread(async () =>
163 if (InfoByJid is not
null)
166 this.ContactSelected =
true;
190 private decimal? amount;
196 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
197 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
198 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
199 private bool amountOk;
201 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
203 base.OnPropertyChanged(e);
205 switch (e.PropertyName)
207 case nameof(this.IsConnected):
208 this.AcceptCommand.NotifyCanExecuteChanged();
209 this.PayOnlineCommand.NotifyCanExecuteChanged();
210 this.SubmitCommand.NotifyCanExecuteChanged();
213 case nameof(this.HasQrCode):
214 this.PayOnlineCommand.NotifyCanExecuteChanged();
215 this.GenerateQrCodeCommand.NotifyCanExecuteChanged();
216 this.ShareCommand.NotifyCanExecuteChanged();
219 case nameof(this.AmountText):
220 if (
CommonTypes.TryParse(this.AmountText, out decimal D2) && D2 > 0)
223 this.AmountOk =
true;
226 this.AmountOk =
false;
228 this.AmountAndCurrency = this.AmountText +
" " + this.Currency;
231 case nameof(this.AmountExtraText):
232 if (string.IsNullOrEmpty(this.AmountExtraText))
234 this.AmountExtra =
null;
235 this.AmountExtraOk =
true;
239 this.AmountExtra = D3;
240 this.AmountExtraOk =
true;
243 this.AmountExtraOk =
false;
245 this.AmountExtraAndCurrency = this.AmountExtraText +
" " + this.Currency;
263 public string BalanceString =>
264 this.BalanceDecimal +
" " + this.Currency;
266 public decimal ReservedDecimal =>
267 this.FetchedBalance?.Reserved ?? -1;
269 public string ReservedString
273 if (this.ReservedDecimal == -1)
276 return this.ReservedDecimal +
" " + this.Currency;
280 public bool HasReserved => (this.ReservedDecimal > 0 || this.ReservedDecimal == -1) && this.GetBalanceTask.IsSucceeded;
287 [NotifyPropertyChangedFor(nameof(BalanceString))]
288 [NotifyPropertyChangedFor(nameof(ReservedDecimal))]
289 [NotifyPropertyChangedFor(nameof(ReservedString))]
290 [NotifyPropertyChangedFor(nameof(HasReserved))]
297 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
298 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
299 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
300 private string? amountText;
306 private string? amountAndCurrency;
312 private bool amountPreset;
318 private decimal? amountExtra;
324 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
325 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
326 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
327 private bool amountExtraOk;
333 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
334 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
335 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
336 private string? amountExtraText;
342 private string? amountExtraAndCurrency;
348 private bool amountExtraPreset;
354 private string? currency;
360 private DateTime created;
366 private DateTime expires;
372 private string? expiresStr;
384 private string? from;
408 private bool contactSelected =
false;
414 private bool toPreset;
426 private string? friendlyName;
432 private bool complete;
438 private string? message;
444 private bool encryptMessage;
450 private bool canEncryptMessage;
456 private bool hasMessage;
462 private bool messagePreset;
468 [NotifyCanExecuteChangedFor(nameof(PayOnlineCommand))]
469 [NotifyCanExecuteChangedFor(nameof(GenerateQrCodeCommand))]
470 [NotifyCanExecuteChangedFor(nameof(SendPaymentCommand))]
471 private bool notPaid;
477 private string? eDalerFrontGlyph;
483 private string? eDalerBackGlyph;
491 private async Task FromClick()
495 string? Value = this.From;
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))
506 if (
System.Uri.TryCreate(
"https://" + Value, UriKind.Absolute, out Uri) && await Launcher.TryOpenAsync(Uri))
509 await Clipboard.SetTextAsync(Value);
523 [RelayCommand(CanExecute = nameof(IsConnected))]
524 private async Task Accept()
528 if (this.Uri is
null)
531 if (!await this.authenticationService.AuthenticateUserAsync(
AuthenticationPurpose.AcceptEDalerUri,
true))
551 [RelayCommand(CanExecute = nameof(IsConnected))]
552 private async Task Decline()
560 [RelayCommand(CanExecute = nameof(CanPayOnline))]
561 private async Task PayOnline()
577 if (this.EncryptMessage && this.ToType ==
EntityType.LegalId)
583 this.Currency ??
string.Empty, 3, this.Message ??
string.Empty);
593 MainThread.BeginInvokeOnMainThread(async () =>
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);
617 this.NotPaid =
false;
646 [RelayCommand(CanExecute = nameof(CanGenerateQrCode))]
647 private async Task GenerateQrCode()
662 if (this.EncryptMessage && this.ToType ==
EntityType.LegalId)
666 this.Currency ??
string.Empty, 3, this.Message ??
string.Empty);
670 Uri = await
ServiceRef.
XmppService.CreateFullEDalerPaymentUri(this.To!, this.Amount ?? 0, this.AmountExtra,
671 this.Currency ??
string.Empty, 3, this.Message ??
string.Empty);
679 MainThread.BeginInvokeOnMainThread(async () =>
681 this.GenerateQrCode(Uri);
683 if (this.shareQrCode is not
null)
684 await this.shareQrCode.ShowQrCode();
695 private bool CanPayOnline => this.AmountOk && this.AmountExtraOk && !this.HasQrCode && this.IsConnected && this.NotPaid;
696 private bool CanGenerateQrCode => this.AmountOk && this.AmountExtraOk && !this.HasQrCode && this.NotPaid;
697 private bool CanShare => this.HasQrCode;
702 [RelayCommand(CanExecute = nameof(CanShare))]
703 private async Task Share()
705 if (this.QrCodeBin is
null)
710 string? Message = this.Message ?? this.AmountAndCurrency;
713 string.Format(CultureInfo.CurrentCulture, Message ??
string.Empty,
this.Amount,
this.Currency),
726 [RelayCommand(CanExecute = nameof(IsConnected))]
727 private async Task Submit()
729 if (this.Uri is
null)
759 private async Task ShowCode()
761 if (this.Uri is
null)
764 if (!await this.authenticationService.AuthenticateUserAsync(
AuthenticationPurpose.ShowUriAsQr,
true))
771 MainThread.BeginInvokeOnMainThread(async () =>
773 this.GenerateQrCode(this.Uri);
775 if (this.shareQrCode is not
null)
776 await this.shareQrCode.ShowQrCode();
787 private bool CanSendPayment()
789 return this.uriToSend is not
null && this.AmountOk && this.AmountExtraOk && this.NotPaid;
795 [RelayCommand(CanExecute = nameof(CanSendPayment))]
796 private async Task SendPayment()
805 if (!await this.authenticationService.AuthenticateUserAsync(
AuthenticationPurpose.SendPayment,
true))
812 if (this.EncryptMessage && this.ToType ==
EntityType.LegalId)
816 this.Currency!, 3, this.Message ??
string.Empty);
823 this.Currency!, 3,
this.Message ??
string.Empty);
827 Uri = await
ServiceRef.
XmppService.CreateFullEDalerPaymentUri(this.To!, this.Amount ?? 0, this.AmountExtra,
828 this.Currency!, 3, this.Message ??
string.Empty);
834 this.uriToSend?.TrySetResult(Uri);
835 this.messageToSend?.TrySetResult(this.Message);
862 case "AmountExtraText":
875 #region ILinkableView
885 private async Task SelectRecipient()
889 TaskCompletionSource<ContactInfoModel?> Selected =
new();
902 this.ToContact = Contact;
903 this.ContactSelected =
true;
905 if (!
string.IsNullOrEmpty(Contact.
LegalId))
910 else if (!
string.IsNullOrEmpty(Contact.
BareJid))
922 this.ToPreset =
false;
932 private Task ClearRecipient()
934 this.To =
string.Empty;
935 this.ToContact =
null;
936 this.ContactSelected =
false;
938 return Task.CompletedTask;
945 private async Task LoadBalanceAsync(TaskContext<bool> Ctx)
954 MainThread.BeginInvokeOnMainThread(() =>
956 this.FetchedBalance = CurrentBalance;
957 this.BalanceUpdated = DateTime.UtcNow;
958 ServiceRef.TagProfile.LastEDalerBalanceDecimal = this.
BalanceDecimal;
959 ServiceRef.TagProfile.LastEDalerBalanceUpdate = DateTime.UtcNow;
966 private async Task ScanQr()
972 if (
string.IsNullOrEmpty(Url))
975 string? NeuroId =
null;
982 int i = Url.IndexOf(
':');
983 NeuroId = Url[(i + 1)..].Trim();
986 NeuroId = Url.Trim();
988 if (
string.IsNullOrEmpty(NeuroId))
998 this.ToPreset =
false;
999 this.ToContact =
null;
1000 this.ContactSelected =
false;
1009 catch (Exception Ex2)
1016 catch (Exception Ex)
Contains information about a balance.
Represents a transaction in the eDaler network.
static readonly TimeSpan XmppConnect
XMPP Connect timeout
const string IotId
The IoT ID URI Scheme (iotid)
A set of never changing property constants and helpful values.
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.
Base class that references services in the app.
static IServiceProvider Provider
The service provider for the app. This is set before the app is started, and will be used to resolve ...
static ILogService LogService
Log service.
static INetworkService NetworkService
Network service.
static IUiService UiService
Service serializing and managing UI-related tasks.
static INavigationService NavigationService
The navigation service for navigating between pages.
static IPopupService PopupService
Popup service for presenting application popups.
static ITagProfile TagProfile
TAG Profile service.
static IReportingStringLocalizer Localizer
Localization service
static IXmppService XmppService
The XMPP service for XMPP communication.
static IPlatformSpecific PlatformSpecific
Localization service
Converts values to strings.
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.
Holds navigation parameters for the calculator.
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.
EDalerUri? Uri
The edaler URI
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.
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Event arguments for legal identity petition responses
LegalIdentity RequestedIdentity
Requested identity, if accepted, null if rejected.
bool Response
If accepted (true) or rejected (false).
string Id
ID of the legal identity
override bool Equals(object obj)
Abstract base class for contractual parameters
The requesting entity does not possess the necessary permissions to perform an action that only certa...
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.
EntityType
Type of entity referred to in transaction.
BackMethod
Navigation Back Method
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.