Exemple #1
0
        protected async void ResendVerification(object sender, EventArgs e)
        {
            const string errorMessage = "Sorry, an error occurred when resending your verification code.";

            btnResend.IsVisible    = false;
            prgResending.IsVisible = true;

            try
            {
                var response = await _authClient.ResendVerificationCode();

                ToastService.Success(response.Message);
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : errorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : errorMessage);
            }
            finally
            {
                btnResend.IsVisible    = true;
                prgResending.IsVisible = false;
            }
        }
Exemple #2
0
        private async Task OnAvatarUpload(IEnumerable <UploadFile> files)
        {
            // 示例代码,使用 base64 格式
            var file = files.FirstOrDefault();

            if (file != null && file.File != null)
            {
                var format = file.File.ContentType;
                if (CheckValidAvatarFormat(format))
                {
                    ReadAvatarToken ??= new CancellationTokenSource();
                    if (ReadAvatarToken.IsCancellationRequested)
                    {
                        ReadAvatarToken.Dispose();
                        ReadAvatarToken = new CancellationTokenSource();
                    }

                    await file.RequestBase64ImageFileAsync(format, 640, 480, MaxFileLength, ReadAvatarToken.Token);
                }
                else
                {
                    file.Code  = 1;
                    file.Error = "文件格式不正确";
                }

                if (file.Code != 0)
                {
                    await ToastService.Error("头像上传", $"{file.Error} {format}");
                }
            }
        }
Exemple #3
0
        private async Task OnCardUpload(IEnumerable <UploadFile> files)
        {
            // 示例代码,使用 IWebHostEnviroment 注入获取硬盘文件夹 示例
            var file = files.FirstOrDefault();

            if (file != null && file.File != null)
            {
                // 生成写入文件名称
                var uploaderFolder = Path.Combine(WebHost.WebRootPath, $"images{Path.DirectorySeparatorChar}uploader");
                file.FileName = $"{Path.GetFileNameWithoutExtension(file.OriginFileName)}-{DateTimeOffset.Now:yyyyMMddHHmmss}{Path.GetExtension(file.OriginFileName)}";
                var fileName = Path.Combine(uploaderFolder, file.FileName);

                ReadCardToken ??= new CancellationTokenSource();
                var ret = await file.SaveToFile(fileName, 20 * 1024 * 1024, ReadCardToken.Token);

                if (ret)
                {
                    // 保存成功
                    file.PrevUrl = $"images/uploader/{file.FileName}";
                }
                else
                {
                    await ToastService.Error("上传文件", $"保存文件失败 {file.OriginFileName}");
                }
            }
        }
Exemple #4
0
        protected async void Remove(object sender, EventArgs e)
        {
            var response = await DisplayAlert("Confirm Removal",
                                              "Are you sure you want to remove this book. This action is irreversible.", "Proceed", "Cancel");

            if (!response)
            {
                return;
            }

            const string genericErrorMessage =
                "Sorry, an error occurred when removing from your library. Try again later.";

            try
            {
                ToastService.Success("Removing from library...");
                await _bookService.Remove(_book.Id);

                ToastService.Success("Book successfully removed from your library.");
                await Navigation.PopAsync();
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
        }
Exemple #5
0
        private async Task <bool> SaveToFile(UploadFile file)
        {
            // Server Side 使用
            // Web Assembly 模式下必须使用 webapi 方式去保存文件到服务器或者数据库中
            // 生成写入文件名称
            var ret = false;

            if (!string.IsNullOrEmpty(SiteOptions.WebRootPath))
            {
                var uploaderFolder = Path.Combine(SiteOptions.WebRootPath, $"images{Path.DirectorySeparatorChar}uploader");
                file.FileName = $"{Path.GetFileNameWithoutExtension(file.OriginFileName)}-{DateTimeOffset.Now:yyyyMMddHHmmss}{Path.GetExtension(file.OriginFileName)}";
                var fileName = Path.Combine(uploaderFolder, file.FileName);

                ReadToken ??= new CancellationTokenSource();
                ret = await file.SaveToFile(fileName, MaxFileLength, ReadToken.Token);

                if (ret)
                {
                    // 保存成功
                    file.PrevUrl = $"images/uploader/{file.FileName}";
                }
                else
                {
                    await ToastService.Error("上传文件", $"保存文件失败 {file.OriginFileName}");
                }
            }
            else
            {
                await ToastService.Information("保存文件", "当前模式为 WebAssembly 模式,请调用 Webapi 模式保存文件到服务器端或数据库中");
            }
            return(ret);
        }
        protected async void UpdatePassword(object sender, EventArgs e)
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when updating your password. Try again later.";
            var currentPassword    = txtCurrentPassword.Text;
            var newPassword        = txtNewPassword.Text;
            var confirmNewPassword = txtConfirmNewPassword.Text;

            if (string.IsNullOrWhiteSpace(currentPassword))
            {
                ToastService.Error("A current password is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(newPassword))
            {
                ToastService.Error("A new password is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(confirmNewPassword))
            {
                ToastService.Error("A password confirmation is required.");
                return;
            }

            if (newPassword != confirmNewPassword)
            {
                ToastService.Error("The password and confirmation don't match.");
                return;
            }

            prgUpdatePassword.IsVisible = true;
            btnUpdatePassword.IsVisible = false;

            try
            {
                var response = await _userService.UpdatePassword(currentPassword, newPassword);

                ToastService.Success(response.Message);
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                btnUpdatePassword.IsVisible = true;
                prgUpdatePassword.IsVisible = false;
            }
        }
Exemple #7
0
        protected async void Verify(object sender, EventArgs e)
        {
            const string errorMessage     = "Sorry, an error occurred when verifying your email address. Try again later.";
            var          verificationCode = txtVerificationCode.Text;

            if (string.IsNullOrWhiteSpace(verificationCode))
            {
                ToastService.Error("A verification code is required.");
                return;
            }

            if (verificationCode.Length != Constants.VerificationCodeLength)
            {
                ToastService.Error($"Verification code must be {Constants.VerificationCodeLength} digits long.");
                return;
            }

            if (!verificationCode.All(char.IsDigit))
            {
                ToastService.Error("Verification code can only contain digits.");
                return;
            }

            btnVerify.IsVisible  = false;
            prgLoading.IsVisible = true;

            try
            {
                var response = await _authClient.VerifyEmail(verificationCode);

                TokenService.SetEmailVerified(true);

                ToastService.Success(response.Message);

                // go back to login
                Application.Current.MainPage = new NavigationPage(new Root());
                await Navigation.PopAsync();
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : errorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : errorMessage);
            }
            finally
            {
                btnVerify.IsVisible  = true;
                prgLoading.IsVisible = false;
            }
        }
Exemple #8
0
        private async void LoadMore(object sender, EventArgs e)
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when retrieving more library items. Try again later.";

            try
            {
                // ensure multiple calls are not made until the current is done
                if (_isLoadingMore)
                {
                    return;
                }

                // check to see if the list is less than than the max items per page meaning there
                // are no more items to get
                if (_books.Count % BookService.BooksPerPage != 0)
                {
                    return;
                }

                _isLoadingMore           = true;
                prgLoadingMore.IsVisible = true;
                var search   = GetSearchText();
                var response = await _bookService.GetNextPage(search);

                var books = response.ToList();

                if (books.Any())
                {
                    foreach (var book in books)
                    {
                        _books.Add(book);
                    }

                    lstBooks.ItemsSource = _books;
                }
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                _isLoadingMore           = false;
                prgLoadingMore.IsVisible = false;
            }
        }
        private static void HandleOtherException(Exception exception)
        {
            DispatcherHelper.Invoke(() =>
            {
                var toast = new ToastService();
                string errorMsg = string.Format("ERROR: {0}", exception.Message);
                toast.Error(errorMsg);
            });

            Logger.Instance.Fatal(exception.ToString());
        }
Exemple #10
0
        public async Task LoadData()
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when retrieving your library. Try again later.";

            rfsLibrary.IsRefreshing = true;

            try
            {
                var search   = GetSearchText();
                var response = await _bookService.GetFirstPage(search);

                var books = response.ToList();

                if (books.Any())
                {
                    _books = new ObservableCollection <BookViewModel>(books);
                    lstBooks.ItemsSource       = _books;
                    lblNoItems.IsVisible       = false;
                    lblNoSearchItems.IsVisible = false;
                }
                else
                {
                    _books.Clear();
                    lstBooks.ItemsSource = _books;

                    if (string.IsNullOrWhiteSpace(GetSearchText()))
                    {
                        lblNoItems.IsVisible = true;
                    }
                    else
                    {
                        lblNoSearchItems.IsVisible = true;
                    }
                }
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                rfsLibrary.IsRefreshing = false;
            }
        }
        protected async void Add(object sender, EventArgs e)
        {
            const string genericErrorMessage = "Sorry, an error occurred when adding the item. Try again later.";
            var          title  = txtTitle.Text;
            var          author = txtAuthor.Text;
            var          isbn   = txtIsbn.Text;

            if (string.IsNullOrWhiteSpace(title))
            {
                ToastService.Error("A book title is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(author))
            {
                ToastService.Error("An author name is required.");
                return;
            }

            stkBtns.IsVisible   = false;
            prgAdding.IsVisible = true;

            try
            {
                var response = await _wishListService.Add(title, author, isbn);

                // notify user
                ToastService.Success("Book successfully added to your wish list.");

                // send data back
                Dismiss(response);
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : genericErrorMessage);
            }
            finally
            {
                stkBtns.IsVisible   = true;
                prgAdding.IsVisible = false;
            }
        }
        protected async void Add(object sender, EventArgs e)
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when adding the book to your library. Try again later.";
            var title       = txtTitle.Text;
            var summary     = txtSummary.Text;
            var isbn        = txtIsbn.Text;
            var publishYear = txtPublishYear.Text;
            var authors     = txtAuthors.Text;
            var publisher   = txtPublisher.Text;
            var pageCount   = txtPageCount.Text;

            if (string.IsNullOrWhiteSpace(title))
            {
                ToastService.Error("A title is required.");
                return;
            }

            stkBtns.IsVisible   = false;
            prgAdding.IsVisible = true;

            try
            {
                await _bookService.AddManual(title, summary, isbn, publishYear, authors, publisher, pageCount);

                // notify user
                ToastService.Success("Book successfully added to your library.");

                // send data back
                Dismiss("");
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : genericErrorMessage);
            }
            finally
            {
                stkBtns.IsVisible   = true;
                prgAdding.IsVisible = false;
            }
        }
        private async void ScannerOnDisappearing(object sender, EventArgs e)
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when adding the book to your library. Try again later.";
            var scanner = sender as Scanner;
            var result  = scanner?.ScanResult;

            // try disconnecting the event handler
            if (scanner != null)
            {
                scanner.Disappearing -= ScannerOnDisappearing;
            }

            if (string.IsNullOrEmpty(result))
            {
                return;
            }

            try
            {
                // notify the user
                ToastService.Info($"Scanned ISBN: {result}. Adding to your library.");

                // call the service
                await _bookService.AddByIsbn(result);

                ToastService.Success("Book successfully added to your library");

                // refresh the library
                await pageHome.LoadData();

                await pageLibrary.LoadData();
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
        }
Exemple #14
0
        protected async void AttemptRequest(object sender, EventArgs e)
        {
            var email = txtEmail.Text;

            if (string.IsNullOrWhiteSpace(email))
            {
                ToastService.Error("An email address is required.");
                return;
            }

            btnRequest.IsVisible = false;
            prgLoading.IsVisible = true;

            try
            {
                var response = await _authClient.ForgotPassword(email);

                ToastService.Success(response.Message);

                // go
                await Navigation.PushAsync(new ResetPassword(email));
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0
                    ? error.Message[0]
                    : "Sorry, an error occurred when requesting a reset code. Try again later.");
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message)
                    ? error.Message
                    : "Sorry, an error occurred when requesting a reset code. Try again later.");
            }
            finally
            {
                btnRequest.IsVisible = true;
                prgLoading.IsVisible = false;
            }
        }
        protected async void Add(object sender, EventArgs e)
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when adding the book to your library. Try again later.";
            var isbn = txtIsbn.Text;

            if (string.IsNullOrWhiteSpace(isbn))
            {
                ToastService.Error("An isbn is required.");
                return;
            }

            stkBtns.IsVisible   = false;
            prgAdding.IsVisible = true;

            try
            {
                await _bookService.AddByIsbn(isbn);

                // notify user
                ToastService.Success("Book successfully added to your library.");

                // send data back
                Dismiss("");
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : genericErrorMessage);
            }
            finally
            {
                stkBtns.IsVisible   = true;
                prgAdding.IsVisible = false;
            }
        }
        public async Task LoadData()
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when retrieving your wish list. Try again later.";

            rfsWishList.IsRefreshing = true;

            try
            {
                var response = await _wishListService.GetAll();

                var wishlist = response.ToList();

                if (wishlist.Any())
                {
                    _wishListItems          = new ObservableCollection <WishListViewModel>(wishlist);
                    lstWishList.ItemsSource = _wishListItems;
                }
                else
                {
                    lblNoItems.IsVisible = true;
                }
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                rfsWishList.IsRefreshing = false;
            }
        }
        protected async void UpdateProfile(object sender, EventArgs e)
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when updating your information. Try again later.";

            prgUpdateProfile.IsVisible = true;
            btnUpdateProfile.IsVisible = false;

            var firstName = txtFirstName.Text;
            var lastName  = txtLastName.Text;

            try
            {
                var user = await _userService.UpdateProfile(firstName, lastName);

                // update the details locally
                TokenService.SetUserDetails(user.FirstName, user.LastName);

                // notify user
                ToastService.Success("Profile updated successfully.");
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                btnUpdateProfile.IsVisible = true;
                prgUpdateProfile.IsVisible = false;
            }
        }
        protected override async void OnAppearing()
        {
            base.OnAppearing();

            try
            {
                // load the home page data
                await pageHome.LoadData();

                // load the library page data
                await pageLibrary.LoadData();

                // load the wishlist page data
                await pageWishList.LoadData();

                // load profile page data
                await pageProfile.LoadData();
            }
            catch (Exception ex)
            {
                ToastService.Error(ex.Message);
            }
        }
        public async Task LoadData()
        {
            const string genericErrorMessage =
                "Sorry, an error occurred when retrieving your information. Try again later.";

            // pull local user info
            var(firstName, lastName) = TokenService.GetUserDetails();
            var emailAddress = TokenService.GetEmail();

            DisplayUser(string.Empty, firstName, lastName, emailAddress);

            // pull remote user data
            prgLoading.IsVisible = true;

            try
            {
                var user = await _userService.GetProfile();

                DisplayUser(user);
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                prgLoading.IsVisible = false;
            }
        }
Exemple #20
0
        protected async void AttemptLogin(object sender, EventArgs e)
        {
            const string genericErrorMessage = "Sorry, an error occurred when logging you in. Try again later.";
            var          email    = txtEmail.Text;
            var          password = txtPassword.Text;

            if (string.IsNullOrWhiteSpace(email))
            {
                ToastService.Error("An email address is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(password))
            {
                ToastService.Error("A password is required.");
                return;
            }

            btnLogin.IsVisible   = false;
            prgLoading.IsVisible = true;

            try
            {
                var response = await _authClient.Login(email, password);

                TokenService.SetUserDetails(response.FirstName, response.LastName);
                TokenService.SetEmail(response.EmailAddress);
                TokenService.SetEmailVerified(response.IsEmailVerified);
                await TokenService.SetAuthToken(response.AuthToken);

                ToastService.Success("Logged in successfully.");

                if (response.IsEmailVerified)
                {
                    // send to home page
                    Navigation.InsertPageBefore(new Root(), this);
                    await Navigation.PopAsync();
                }
                else
                {
                    await Navigation.PushAsync(new VerifyEmail());
                }
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : genericErrorMessage);
            }
            finally
            {
                btnLogin.IsVisible   = true;
                prgLoading.IsVisible = false;
            }
        }
Exemple #21
0
        public async Task LoadData()
        {
            // load remote data
            const string genericErrorMessage =
                "Sorry, an error occurred when retrieving your data. Try again later.";

            rfsHome.IsRefreshing = true;

            try
            {
                // get the most recent books
                var bookResponse = await _bookService.GetRecent();

                var books = bookResponse.ToList();

                if (books.Any())
                {
                    lstRecentBooks.ItemsSource = books;
                    lblNoRecentBooks.IsVisible = false;
                }
                else
                {
                    lblNoRecentBooks.IsVisible = true;
                }

                // get the top authors
                var authorResponse = await _authorService.GetTop();

                var authors = authorResponse.ToList();

                if (authors.Any())
                {
                    lstTopAuthors.ItemsSource = authors;
                    lblNoAuthors.IsVisible    = false;
                }
                else
                {
                    lblNoAuthors.IsVisible = true;
                }

                // get the top publishers
                var publisherResponse = await _publisherService.GetTop();

                var publishers = publisherResponse.ToList();

                if (publishers.Any())
                {
                    lstTopPublishers.ItemsSource = publishers;
                    lblNoPublishers.IsVisible    = false;
                }
                else
                {
                    lblNoPublishers.IsVisible = true;
                }

                // get the stats for the books
                var stats = await _statisticsService.Get();

                lblBooksCount.Text      = stats.Books.ToString();
                lblAuthorsCount.Text    = stats.Authors.ToString();
                lblPublishersCount.Text = stats.Publishers.ToString();
                lblWishlistCount.Text   = stats.WishListItems.ToString();
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message : genericErrorMessage);
            }
            finally
            {
                rfsHome.IsRefreshing = false;
            }
        }
        protected async void AttemptRegister(object sender, EventArgs e)
        {
            const string genericErrorMessage = "Sorry, an error occurred when registering you. Try again later.";
            var          fullName            = txtFullName.Text;
            var          email           = txtEmail.Text;
            var          password        = txtPassword.Text;
            var          confirmPassword = txtConfirmPassword.Text;

            if (string.IsNullOrWhiteSpace(email))
            {
                ToastService.Error("An email address is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(password))
            {
                ToastService.Error("A password is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(confirmPassword))
            {
                ToastService.Error("A password confirmation is required.");
                return;
            }

            if (password != confirmPassword)
            {
                ToastService.Error("The password and confirmation don't match.");
                return;
            }

            btnRegister.IsVisible = false;
            prgLoading.IsVisible  = true;

            try
            {
                var response = await _authClient.Register(fullName, email, password);

                TokenService.SetUserDetails(response.FirstName, response.LastName);
                TokenService.SetEmail(email);
                TokenService.SetEmailVerified(response.IsEmailVerified);
                await TokenService.SetAuthToken(response.AuthToken);

                ToastService.Success("Account created successfully.");

                // send to verification page
                await Navigation.PushAsync(new VerifyEmail());
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : genericErrorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : genericErrorMessage);
            }
            finally
            {
                btnRegister.IsVisible = true;
                prgLoading.IsVisible  = false;
            }
        }
        protected async void Reset(object sender, EventArgs e)
        {
            const string errorMessage    = "Sorry, an error occurred when requesting a reset code. Try again later.";
            var          resetCode       = txtResetCode.Text;
            var          password        = txtPassword.Text;
            var          confirmPassword = txtConfirmPassword.Text;

            if (string.IsNullOrWhiteSpace(resetCode))
            {
                ToastService.Error("A reset code is required.");
                return;
            }

            if (resetCode.Length != Constants.ResetCodeLength)
            {
                ToastService.Error($"Reset code must be {Constants.ResetCodeLength} digits long.");
                return;
            }

            if (!resetCode.All(char.IsDigit))
            {
                ToastService.Error("Reset code can only contain digits.");
                return;
            }

            if (string.IsNullOrWhiteSpace(password))
            {
                ToastService.Error("A password is required.");
                return;
            }

            if (string.IsNullOrWhiteSpace(confirmPassword))
            {
                ToastService.Error("A password confirmation is required.");
                return;
            }

            if (password != confirmPassword)
            {
                ToastService.Error("The password and confirmation don't match.");
                return;
            }

            btnReset.IsVisible   = false;
            prgLoading.IsVisible = true;

            try
            {
                var response = await _authClient.ResetPassword(_emailAddress, resetCode, password);

                ToastService.Success(response.Message);

                // go back to login
                Navigation.InsertPageBefore(new Login(), this);
                await Navigation.PopAsync();
            }
            catch (ApiException ex) when(ex.IsValidationException())
            {
                var error = await ex.GetContentAsAsync <ValidationErrorViewModel>();

                ToastService.Error(error?.Message?.Length > 0 ? error.Message[0] : errorMessage);
            }
            catch (ApiException ex)
            {
                var error = await ex.GetContentAsAsync <ErrorViewModel>();

                ToastService.Error(!string.IsNullOrWhiteSpace(error?.Message) ? error.Message : errorMessage);
            }
            finally
            {
                btnReset.IsVisible   = true;
                prgLoading.IsVisible = false;
            }
        }