private async Task PurchaseSubscription(string subCode) { if (_userCache.GetLoggedInAccount() == null) { throw new InvalidOperationException("User must be logged in to purchase a subscription."); } var sub = await _purchaseService.PurchaseItem(subCode, ItemType.Subscription, "payload"); if (sub == null) { throw new InvalidOperationException($"Something went wrong when attempting to purchase. Please try again."); } var model = new Models.SubscriptionModel() { PurchaseId = sub.Id, PurchaseToken = sub.PurchaseToken, SubscriptionType = SubscriptionUtility.GetTypeFromProductId(sub.ProductId), StartDateTime = DateTimeOffset.UtcNow, PurchasedDateTime = DateTimeOffset.UtcNow, EndDateTime = DateTimeOffset.UtcNow.AddMonths(1), PurchaseSource = Device.RuntimePlatform == Device.Android ? Models.PurchaseSource.GooglePlay : Models.PurchaseSource.AppStore, UserId = _userCache.GetLoggedInAccount().UserId, Email = _userCache.GetLoggedInAccount().Email }; try { _subCache.Put(model.PurchaseId, model); } catch { } _subService.AddSubscription(model); }
public void LogError(string message, Exception ex, params object[] args) { try { var dict = new Dictionary <string, string>(); dict.Add("source_name", typeof(T).ToString()); dict.Add("message", message); dict.Add("app_platform", Device.RuntimePlatform); dict.Add("exception", ex?.Message); for (int i = 0; i < args.Length; i++) { dict.Add($"metadata_{i}", JsonConvert.SerializeObject(args[i])); } if (_userCache.GetLoggedInAccount() != null) { dict.Add("user_email", _userCache.GetLoggedInAccount().Email); } _service.LogEvent("app_error", dict); #if DEBUG Debug.WriteLine(message); Debug.WriteLine(JsonConvert.SerializeObject(dict)); #endif } catch (Exception e) { #if DEBUG Debug.WriteLine(e.ToString()); #endif } }
private async Task SetVisualStateForValidation() { try { var user = _userService.GetLoggedInAccount(); if (user == null) { MainLayoutVisible = true; CannotSubmitLayoutVisible = false; return; } var validation = await _orderValidator.ValidateOrderRequest(user); var activeSub = SubscriptionUtility.SubscriptionActive(validation.Subscription); if (validation.RemainingOrders > 0) { MainLayoutVisible = true; CannotSubmitLayoutVisible = false; return; } switch (validation.State) { case ValidationState.NoReportsLeftInPeriod: MainLayoutVisible = false; CannotSubmitHeaderText = "You've been busy!"; CannotSubmitLabelText = $"Sorry, you have used all of your reports for this month."; CannotSubmitLayoutVisible = true; PurchaseOptionsText = $"Additional reports can be purchased at a reduced price of " + $"${SubscriptionUtility.GetSingleReportInfo(validation).Price} per report."; PurchaseOptionsVisible = true; break; case ValidationState.NoSubscriptionAndTrialValid: MainLayoutVisible = false; CannotSubmitHeaderText = "Thanks for trying Fair Squares!"; CannotSubmitLabelText = $"Please claim your free one month subscription trial, or click below to view other options."; CannotSubmitLayoutVisible = true; PurchaseOptionsVisible = false; break; case ValidationState.NoSubscriptionAndTrialAlreadyUsed: MainLayoutVisible = false; CannotSubmitHeaderText = "Thanks for trying Fair Squares!"; CannotSubmitLabelText = $"Click below to view options for getting more reports."; CannotSubmitLayoutVisible = true; PurchaseOptionsVisible = false; break; default: MainLayoutVisible = true; CannotSubmitLayoutVisible = false; break; } } catch (Exception ex) { _logger.LogError("Failed to set visual state on load.", ex); } }
public void LogError(string message, Exception ex, params object[] args) { try { var emailBody = $"<p>User with ID '{_userCache.GetLoggedInAccount()?.UserId ?? "Not logged in user"}' and email '{_userCache.GetLoggedInAccount().Email}' had the following issue: "; emailBody += @"<br/>" + message; foreach (var arg in args) { emailBody += @"<br/>" + JsonConvert.SerializeObject(arg, Formatting.Indented); } emailBody += @"<br/>" + Device.Idiom.ToString() + "</p>"; Task.Run(() => { _notifyService.Notify(new Models.NotificationRequest() { From = $"{Device.RuntimePlatform.ToString()}[email protected]", To = $"*****@*****.**", Message = emailBody, MessageType = Models.MessageType.Email, Subject = $"{Device.RuntimePlatform.ToString()} Critical Error" }); }); } catch (Exception e) { Debug.WriteLine($"An error occurred while logging to email." + e.ToString()); } }
public OrderViewModel(IOrderValidationService validator, ICurrentUserService userCache, IOrderService orderService, IToastService toast, IPageFactory pageFactory, MainThreadNavigator nav, IMessagingSubscriber topicSubscriber, ILogger <OrderViewModel> logger, string deviceType, AlertUtility alertUtility, Action <BaseNavPageType> baseNavigationAction, ICache <Models.Order> orderCache) { _orderValidator = validator; _userService = userCache; _toast = toast; _nav = nav; _orderService = orderService; _pageFactory = pageFactory; _orderCache = orderCache; _alertUtility = alertUtility; _topicSubscriber = topicSubscriber; _baseNavigationAction = baseNavigationAction; _deviceType = deviceType; _logger = logger; PurchaseOptionsCommand = new Command(async() => { var val = await _orderValidator.ValidateOrderRequest(_userService.GetLoggedInAccount()); if (SubscriptionUtility.SubscriptionActive(val.Subscription)) { _nav.Push(_pageFactory.GetPage(PageType.SingleReportPurchase, val)); } else { _nav.Push(_pageFactory.GetPage(PageType.PurchaseOptions, val)); } }); OptionsInfoCommand = new Command(async() => await alertUtility.Display("Roof Option Selection", $"Selecting a roof option allows Fair Squares to determine what roofs you would like measured at the submitted address.{Environment.NewLine}" + $"{Environment.NewLine}Primary Only- Fair Squares will measure the primary structure, including attached garage.{Environment.NewLine}" + $"Detached Garage- Fair Squares will also measure the detached garage on the property.{Environment.NewLine}" + $"Shed/Barn- Fair Squares will also measure a shed or barn on the property.{Environment.NewLine}" + $"{Environment.NewLine}NOTE: Fair Squares only supports measuring one primary structure per report (with a detached garage or shed/barn if needed).", "Ok")); ErrorMessageRowHeight = 0; SelectedOptionIndex = -1; SelectedStateIndex = -1; #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed SetVisualStateForValidation(); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed }
private async Task PurchaseItem(string itemCode) { if (_userCache.GetLoggedInAccount() == null) { throw new InvalidOperationException("User must be logged in to purchase a report."); } var purchase = await _purchaseService.PurchaseItem(itemCode, ItemType.InAppPurchase, "payload"); if (purchase == null) { throw new InvalidOperationException($"Something went wrong when attempting to purchase. Please try again."); } var model = new Models.PurchasedReportModel() { PurchaseId = purchase.Id, PurchaseToken = purchase.PurchaseToken, PurchasedDateTime = DateTimeOffset.UtcNow, PurchaseSource = Device.RuntimePlatform == Device.Android ? Models.PurchaseSource.GooglePlay : Models.PurchaseSource.AppStore, UserId = _userCache.GetLoggedInAccount().UserId, Email = _userCache.GetLoggedInAccount().Email }; try { _prService.AddPurchasedReport(model); } catch (Exception ex) { _emailLogger.LogError($"Purchase succeeded but failed to add purchase to server.", ex, model); } try { _prCache.Put(model.PurchaseId, model); } catch { } }
public AccountViewModel(ICurrentUserService userCache, IOrderValidationService orderValidator, MainThreadNavigator navigation, IPageFactory pageFactory, Action <string> changeLogInStyleClass, Action <string> changeSubStyleClass, ILogger <AccountViewModel> logger, ICacheRefresher cacheRefresher) { _navigation = navigation; _pageFactory = pageFactory; _userCache = userCache; _logger = logger; _cacheRefresher = cacheRefresher; _userCache.OnLoggedIn += async(s, e) => { SetAccountState(e.Account); await SetSubState(e.Account); }; _userCache.OnLoggedOut += async(s, e) => { SetAccountState(null); await SetSubState(null); }; _orderValidator = orderValidator; _changeLoginStyleClass = changeLogInStyleClass; _changeSubStyleClass = changeSubStyleClass; var user = userCache.GetLoggedInAccount(); _logger = logger; OnAppearingBehavior = new Command(async() => { var u = _userCache.GetLoggedInAccount(); SetAccountState(u); await SetSubState(u); }); SetInitialState(user); }
private async Task SubmitFeedback(string feedback) { if (string.IsNullOrWhiteSpace(feedback)) { return; } var user = _userCache.GetLoggedInAccount(); if (user == null) { var response = await _alertUtility.Display("Not Logged In", "Because you are not logged in, Fair Squares staff will not be able to respond to your feedback. " + "Are you sure you want to continue?", "Continue", "Log In"); if (!response) { _nav.Push(_pageFactory.GetPage(PageType.Landing)); return; } } try { _notifier.Notify(new Models.NotificationRequest() { From = "*****@*****.**", To = "*****@*****.**", Message = feedback, MessageType = Models.MessageType.Email, Subject = "Feedback from " + user?.Email }); await _alertUtility.Display("Feedback Submitted", "Thank you for your feedback!", "Ok"); } catch (Exception ex) { _logger.LogError("Failed to submit feedback.", ex, $"Feedback: {feedback}"); } _nav.Pop(); }
public OrderDetailViewModel(Models.Order order, ICache <PropertyModel> propertyCache, ICache <ImageModel> imgCache, IPropertyService propService, IImageService imgService, IToastService toast, MainThreadNavigator nav, IPageFactory pageFactory, Func <string, string, string, Task> alertAction, ICurrentUserService userService, ILogger <OrderDetailViewModel> logger) { _order = order; _propertyCache = propertyCache; _imageCache = imgCache; _propertyService = propService; _pageFactory = pageFactory; _imageService = imgService; _userService = userService; _toastService = toast; _nav = nav; _alertAction = alertAction; _logger = logger; OnAppearingBehavior = new Command(async() => await LoadPropertyAndImage()); // Display message if order isn't fulfilled yet. if (!order.Fulfilled) { StatusMessageVisible = true; MainLayoutVisible = false; // Have to do this because some dummy decided to store dates in EST DateTime displayTime = DateTime.Now; if (order.DateReceived != null) { var timeInfo = TimeZoneInfo.FindSystemTimeZoneById("America/Detroit"); var utc = TimeZoneInfo.ConvertTimeToUtc(order.DateReceived.Value.DateTime); displayTime = TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.Local); } SubmittedDateText = $"Order #{order.OrderId} was submitted on {displayTime.ToString("dddd, MMMM dd yyyy")}" + $" at {displayTime.ToString("h:mm tt")}"; if (order.Status == null) { order.Status = new StatusModel() { Status = Status.Pending }; } switch (order.Status.Status) { case (Status.ActionRequired): StatusMessageText = $"Please respond to the message sent to {_userService.GetLoggedInAccount()?.Email ?? "your logged in email"} to continue with this order."; StatusText = "Action Required"; TimingDisclaimerVisible = false; break; default: StatusMessageText = "Your order has been received and is being processed."; StatusText = "Pending"; TimingDisclaimerVisible = true; break; } return; } else { StatusMessageVisible = false; } _property = _propertyCache.Get(order.OrderId); _image = _property != null?_imageCache.Get(_property.OrderId) : null; SelectedRoofChangedCommand = new Command(() => SetUIMeasurements(_selectedRoofIndex)); if ((_property == null || _image == null) && _order.Fulfilled) { MainLayoutVisible = false; StatusMessageVisible = false; LoadingAnimVisible = true; LoadingAnimRunning = true; } else { MainLayoutVisible = true; if (_property.Roofs.Count > 1) { RoofSelectionVisible = true; RoofsSource = _property.Roofs.Select(x => x.Name).Distinct().ToList(); RoofsSource = new[] { "All" }.Concat(RoofsSource).ToList(); } else { RoofSelectionVisible = false; } SetUIMeasurements(0); } }
public MyOrdersViewModel(IOrderService orderSvc, ICache <Models.Order> orderCache, ICache <PropertyModel> propertyCache, ICache <ImageModel> imageCache, ILogger <MyOrdersViewModel> logger, ICacheRefresher cacheRefresher, IOrderValidationService validator, IPageFactory pageFactory, ICurrentUserService userService, LaunchedFromPushModel pushModel, MainThreadNavigator nav, IMessagingCenter messagingCenter, Action <Action> uiInvoke, Action <BaseNavPageType> baseNavAction) { _orderService = orderSvc; _orderCache = orderCache; _propertyCache = propertyCache; _imageCache = imageCache; _logger = logger; _uiInvoke = uiInvoke; _cacheRefresher = cacheRefresher; _validationService = validator; _pageFactory = pageFactory; _userService = userService; _messagingCenter = messagingCenter; _nav = nav; _baseNavAction = baseNavAction; ExampleReportCommand = new Command(() => { try { var order = JsonConvert.DeserializeObject <Models.Order>(Examples.ExampleOrder); _imageCache.Put(order.OrderId, new ImageModel() { OrderId = order.OrderId, Image = Convert.FromBase64String(Examples.ExampleImage) }); _propertyCache.Put(order.OrderId, JsonConvert.DeserializeObject <PropertyModel>(Examples.ExampleProperty)); _nav.Push(_pageFactory.GetPage(PageType.OrderDetail, order)); } catch (Exception ex) { _logger.LogError($"An error occurred while trying to open example order.", ex); } }); Action refreshAction = async() => { try { OrderListRefreshing = true; var fresh = await _orderService.GetMemberOrders(_userService.GetLoggedInAccount()?.UserId); _orderCache.Put(fresh.ToDictionary(x => x.OrderId, x => x)); SetListViewSource(fresh.ToList()); OrderListRefreshing = false; } catch (Exception ex) { _logger.LogError($"Failed to refresh order list.", ex); } }; OrderListRefreshCommand = new Command(refreshAction); _messagingCenter.Subscribe <App>(this, "CacheInvalidated", async x => { await this.SetViewState(); }); OnAppearingBehavior = new Command(async() => { if (!_pmEventSubscribed) { _pmEventSubscribed = true; pushModel.PropertyChanged += async(s, e) => { if (!string.IsNullOrWhiteSpace(pushModel.OrderId)) { var order = await _orderService.GetOrder(pushModel.OrderId); _orderCache.Put(order.OrderId, order); _nav.Push(_pageFactory.GetPage(PageType.OrderDetail, order)); } }; } await SetViewState(); }); }
private async Task SetViewState() { var orders = new List <Models.Order>(); try { var user = _userService.GetLoggedInAccount(); if (user != null) { if (_cacheRefresher.Invalidated) { MainLayoutVisible = false; FreeReportLayoutVisible = false; LoginLayoutVisible = false; NoOrderLayoutVisible = false; LoadingLayoutVisible = true; LoadingAnimVisible = true; LoadingAnimRunning = true; // Just refresh order cache if cache refresh hasn't been started yet. if (_cacheRefresher.RefreshTask == null || _cacheRefresher.RefreshTask.IsCompleted) { var fresh = await _orderService.GetMemberOrders(user.UserId); _orderCache.Put(fresh.ToDictionary(x => x.OrderId, x => x)); } else { await _cacheRefresher.RefreshTask; } MainLayoutVisible = true; LoadingLayoutVisible = false; LoadingAnimRunning = false; LoadingAnimVisible = false; } orders = _orderCache.GetAll().Select(x => x.Value).ToList(); var anyOrders = orders.Any(); MainLayoutVisible = anyOrders; NoOrderLayoutVisible = !anyOrders; var validation = await _validationService.ValidateOrderRequest(user); FreeReportLayoutVisible = validation.State == ValidationState.FreeReportValid; LoginLayoutVisible = false; } else { FreeReportLayoutVisible = false; MainLayoutVisible = false; LoginLayoutVisible = true; NoOrderLayoutVisible = true; } } catch (Exception ex) { _logger.LogError($"Failed to set view state. {ex.Message}", ex); } finally { LoadingLayoutVisible = false; LoadingAnimRunning = false; LoadingAnimVisible = false; } SetListViewSource(orders); }
private async Task SetInitialState(AccountModel user) { ToolbarInfoCommand = new Command(() => _navigation.Push(_pageFactory.GetPage(PageType.Instruction, false))); LogOutCommand = new Command(async() => { if (_userCache.GetLoggedInAccount() == null) { _navigation.Push(_pageFactory.GetPage(PageType.Landing)); } else { _userCache.LogOut(); SetAccountState(null); await SetSubState(null); } }); SubscriptionCommand = new Command(async() => { if (_userCache.GetLoggedInAccount() == null) { return; } var valid = await _orderValidator.ValidateOrderRequest(_userCache.GetLoggedInAccount()); try { SubscriptionButtonEnabled = false; if (SubscriptionUtility.SubscriptionActive(valid.Subscription)) { _navigation.Push(_pageFactory.GetPage(PageType.ManageSubscription, valid)); } else { _navigation.Push(_pageFactory.GetPage(PageType.PurchaseOptions, valid)); } } catch (Exception ex) { _logger.LogError("Failed to handle subscription click command.", ex); } finally { SubscriptionButtonEnabled = true; } }); FeedbackCommand = new Command(() => { try { FeedbackButtonEnabled = false; _navigation.Push(_pageFactory.GetPage(PageType.Feedback, _navigation)); } catch (Exception ex) { _logger.LogError("Failed to handle feedback click command.", ex); } finally { FeedbackButtonEnabled = true; } }); SetAccountState(user); await SetSubState(user); }