3using System.ComponentModel;
4using System.Globalization;
5using System.Text.RegularExpressions;
6using System.Threading.Tasks;
7using CommunityToolkit.Mvvm.ComponentModel;
8using CommunityToolkit.Mvvm.Input;
24 private static readonly Regex EmailRegex =
new Regex(
"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
25 private bool codeVerified;
37 await base.OnInitializeAsync();
39 LocalizationManager.Current.PropertyChanged += this.LocalizationManagerEventHandler;
43 this.CountDownTimer =
App.
Current.Dispatcher.CreateTimer();
44 this.CountDownTimer.Interval = TimeSpan.FromSeconds(1);
45 this.CountDownTimer.Tick += this.CountDownEventHandler;
51 this.EmailIsValid = EmailRegex.IsMatch(this.EmailText);
52 this.SendCodeCommand.NotifyCanExecuteChanged();
59 LocalizationManager.Current.PropertyChanged -= this.LocalizationManagerEventHandler;
61 if (this.CountDownTimer is not
null)
63 this.CountDownTimer.Stop();
64 this.CountDownTimer.Tick -= this.CountDownEventHandler;
65 this.CountDownTimer =
null;
68 await base.OnDisposeAsync();
71 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
73 base.OnPropertyChanged(e);
74 if (e.PropertyName == nameof(this.IsBusy))
75 this.SendCodeCommand.NotifyCanExecuteChanged();
78 private void LocalizationManagerEventHandler(
object? sender, PropertyChangedEventArgs e)
87 #region Observable Properties
93 [NotifyCanExecuteChangedFor(nameof(SendCodeCommand))]
96 private bool emailIsValid =
true;
102 private bool isEmailReadOnly;
109 [NotifyCanExecuteChangedFor(nameof(SendCodeCommand))]
110 [NotifyCanExecuteChangedFor(nameof(ResendCodeCommand))]
111 private int countDownSeconds;
117 private IDispatcherTimer? countDownTimer;
125 [NotifyCanExecuteChangedFor(nameof(SendCodeCommand))]
126 private string emailText =
string.Empty;
130 #region Localization & Validation
139 if (this.CountDownSeconds > 0)
152 if (!this.EmailIsValid && this.EmailText.Length > 0)
165 #region Computed State
170 public bool CanSendCode => this.EmailIsValid && !this.IsBusy && this.EmailText.Length > 0 && this.CountDownSeconds <= 0;
187 #region Property Change Hooks
189 partial
void OnEmailTextChanged(
string value)
191 bool WasValid = this.EmailIsValid;
192 bool IsValid =
string.IsNullOrWhiteSpace(value) || EmailRegex.IsMatch(value);
193 this.EmailIsValid = IsValid;
194 if (WasValid != IsValid)
197 this.OnPropertyChanged(nameof(this.ShowValidationError));
199 this.SendCodeCommand.NotifyCanExecuteChanged();
210 private async Task SendCode()
212 if (!this.EmailIsValid)
228 new Dictionary<string, object>()
230 {
"EMail", this.EmailText },
231 {
"AppName", Constants.Application.Name },
232 {
"Language", CultureInfo.CurrentCulture.TwoLetterISOLanguageName }
236 new KeyValuePair<string, string>(
"Accept",
"application/json"));
239 if (SendResult.
Decoded is Dictionary<string, object> SendResponse &&
240 SendResponse.TryGetValue(
"Status", out
object? Obj) && Obj is
bool SendStatus && SendStatus)
242 OnboardingViewModel? Coordinator = this.CoordinatorViewModel;
243 if (Coordinator is not
null)
246 bool HasAccount = !
string.IsNullOrEmpty(Profile.
Account);
249 this.IsEmailReadOnly =
true;
253 this.IsEmailReadOnly =
false;
258 VerifyCodeNavigationArgs
NavigationArgs =
new(
this, this.EmailText);
261 if (!
string.IsNullOrEmpty(Code))
263 await this.VerifyCodeAsync(Code).ConfigureAwait(
false);
289 [RelayCommand(CanExecute = nameof(CanResendCode))]
290 private async Task ResendCode()
304 new Dictionary<string, object>()
306 {
"EMail", this.EmailText },
307 {
"AppName", Constants.Application.Name },
308 {
"Language", CultureInfo.CurrentCulture.TwoLetterISOLanguageName }
312 new KeyValuePair<string, string>(
"Accept",
"application/json"));
315 if (SendResult.
Decoded is Dictionary<string, object> SendResponse &&
316 SendResponse.TryGetValue(
"Status", out
object? Obj) && Obj is
bool SendStatus && SendStatus)
340 private async Task VerifyCodeAsync(
string code)
345 this.CoordinatorViewModel?.SetIsBusy(
true);
348 new Dictionary<string, object>()
350 {
"EMail", this.EmailText },
351 {
"Code", int.Parse(code, NumberStyles.None, CultureInfo.InvariantCulture) }
355 new KeyValuePair<string, string>(
"Accept",
"application/json"));
358 if (VerifyResult.
Decoded is Dictionary<string, object> VerifyResponse &&
359 VerifyResponse.TryGetValue(
"Status", out
object? Obj) && Obj is
bool VerifyStatus && VerifyStatus)
361 ServiceRef.TagProfile.EMail = this.EmailText;
362 this.codeVerified =
true;
363 this.OnPropertyChanged(nameof(this.CanContinue));
365 if (this.CoordinatorViewModel is not
null)
367 if (this.CoordinatorViewModel.HasPendingTransfer)
369 bool Connected = await this.CoordinatorViewModel.TryFinalizeTransferAsync(
true).ConfigureAwait(
false);
370 ServiceRef.
LogService.LogInformational(
"Transfer connection attempted after email verification.",
371 new KeyValuePair<string, object?>(
"Success", Connected));
374 await this.CoordinatorViewModel.GoToStepCommand.ExecuteAsync(
OnboardingStep.DefinePassword).ConfigureAwait(
false);
379 await this.CoordinatorViewModel.GoToStepCommand.ExecuteAsync(
OnboardingStep.NameEntry).ConfigureAwait(
false);
400 this.CoordinatorViewModel?.SetIsBusy(
false);
409 private void StartTimer()
411 if (this.CountDownTimer is not
null)
413 this.CountDownSeconds = 30;
414 if (!this.CountDownTimer.IsRunning)
415 this.CountDownTimer.Start();
419 private void CountDownEventHandler(
object? sender, EventArgs e)
421 if (this.CountDownTimer is not
null)
423 if (this.CountDownSeconds > 0)
424 this.CountDownSeconds--;
426 this.CountDownTimer.Stop();
Represents an instance of the Neuro-Access app.
static void ValidateCertificateCallback(object? Sender, RemoteCertificateEventArgs Args)
Callback for validating SSL certificates. This method is called when a remote certificate is received...
static new? App Current
Gets the current application instance.
const string IdDomain
Neuro-Access domain.
A set of never changing property constants and helpful values.
A strongly-typed resource class, for looking up localized strings, etc.
static string EmailValidationFormat
Looks up a localized string similar to Please enter a valid email.
static string OnboardingEmailPageDetails
Looks up a localized string similar to We’ll send you a verification code.
static string SendCode
Looks up a localized string similar to Send Code.
static string SendCodeSeconds
Looks up a localized string similar to Send Code ({0}).
static string NetworkSeemsToBeMissing
Looks up a localized string similar to Network seems to be missing. Please check your configuration a...
static string OnboardingEmailPageTitle
Looks up a localized string similar to Please verify email.
static string Ok
Looks up a localized string similar to OK.
static string UnableToVerifyCode
Looks up a localized string similar to The code was not correct..
static string SomethingWentWrongWhenSendingPhoneCode
Looks up a localized string similar to Something went wrong when sending a verification code to this ...
static string ErrorTitle
Looks up a localized string similar to An error has occurred.
Base class that references services in the app.
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 ITagProfile TagProfile
TAG Profile service.
static IReportingStringLocalizer Localizer
Localization service
An base class holding page specific navigation parameters.
Onboarding step ViewModel for validating e-mail. Updated to mirror registration validation (continuou...
ValidateEmailOnboardingStepViewModel()
Initializes a new instance of the ValidateEmailOnboardingStepViewModel class.
bool CanResendCode
Can resend verification code (timer complete).
override async Task OnInitializeAsync()
Method called when view is initialized for the first time. Use this method to implement registration ...
string LocalizedSendCodeText
Localized send code button text (with countdown state).
override async Task OnDisposeAsync()
Method called when the view is disposed, and will not be used more. Use this method to unregister eve...
bool CanSendCode
Can send a verification code.
bool CanContinue
Can continue to next onboarding step (code verified).
string LocalizedValidationError
Localized validation error text for email.
bool ShowValidationError
If validation error should be shown.
Contains information about a response to a content request.
object Decoded
Decoded object.
void AssertOk()
Asserts response is OK.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
The TAG Profile is the heart of the digital identity for a specific user/device. Use this instance to...
string? Account
The account name for this profile
string? EMail
Verified e-mail address.
Task GoToAsync(string Route)
Navigates to the specified route and pushes the page onto the navigation stack.
Task< bool > DisplayAlert(string Title, string Message, string? Accept=null, string? Cancel=null)
Displays an alert/message box to the user.
BackMethod
Navigation Back Method
OnboardingScenario
Distinct onboarding scenarios.
OnboardingStep
Unified onboarding steps matching the registration journey.