static App() { try { Container = new Container(); var analyticsSvc = DependencyService.Get <IAnalyticsService>(); var userService = new CurrentUserService(); var orderService = new AzureOrderService(new HttpClient(), _orderEndpoint, _apiKey); var propertyService = new PropertyService(new HttpClient(), _propertyEndpoint, new AnalyticsLogger <PropertyService>(analyticsSvc, userService)); var imageService = new BlobImageService(new HttpClient(), _blobEndpoint, new AnalyticsLogger <BlobImageService>(analyticsSvc, userService)); var subService = new SubscriptionService(new HttpClient(), _subEndpoint, new AnalyticsLogger <SubscriptionService>(analyticsSvc, userService)); var prService = new PurchasedReportService(new HttpClient(), _purchasedReportsEndpoint, new AnalyticsLogger <PurchasedReportService>(analyticsSvc, userService)); var authenticator = new OAuth2Authenticator(Configuration.ClientId, null, Configuration.Scope, new Uri(GoogleAuthorizeUrl), new Uri(Configuration.RedirectNoPath + ":" + Configuration.RedirectPath), new Uri(GoogleAccessTokenUrl), null, true); var notifyService = new NotificationService(new HttpClient(), _notifyEndpoint, _apiKey); var purchaseEmailLogger = new EmailLogger <PurchasingService>(notifyService, userService); var purchaseService = new PurchasingService(CrossInAppBilling.Current, purchaseEmailLogger); authenticator.Completed += (s, e) => { if (e.IsAuthenticated) { userService.LogIn(e.Account); } }; // Setup caches and begin process of filling them. var dbBasePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var propertyCache = new LocalSqlCache <PropertyModel>(Path.Combine(dbBasePath, "property.db3"), new AnalyticsLogger <LocalSqlCache <PropertyModel> >(analyticsSvc, userService)); var orderCache = new LocalSqlCache <Models.Order>(Path.Combine(dbBasePath, "order.db3"), new AnalyticsLogger <LocalSqlCache <Models.Order> >(analyticsSvc, userService)); var imageCache = new LocalSqlCache <ImageModel>(Path.Combine(dbBasePath, "images.db3"), new AnalyticsLogger <LocalSqlCache <ImageModel> >(analyticsSvc, userService)); var subCache = new LocalSqlCache <SubscriptionModel>(Path.Combine(dbBasePath, "subs.db3"), new AnalyticsLogger <LocalSqlCache <SubscriptionModel> >(analyticsSvc, userService)); var settingsCache = new LocalSqlCache <SettingsModel>(Path.Combine(dbBasePath, "sets.db3"), new AnalyticsLogger <LocalSqlCache <SettingsModel> >(analyticsSvc, userService)); var prCache = new LocalSqlCache <PurchasedReportModel>(Path.Combine(dbBasePath, "purchasedreports.db3"), new AnalyticsLogger <LocalSqlCache <PurchasedReportModel> >(analyticsSvc, userService)); var startUpLogger = new AnalyticsLogger <App>(analyticsSvc, userService); Action ClearCaches = () => { try { orderCache.Clear(); propertyCache.Clear(); imageCache.Clear(); subCache.Clear(); prCache.Clear(); } catch { } }; Func <AccountModel, Task> RefreshCaches = user => { var userId = user.UserId; var prTask = Task.Run(() => { try { var prs = prService.GetPurchasedReports(userId); prCache.Put(prs.ToDictionary(x => x.PurchaseId, x => x)); } catch (Exception ex) { startUpLogger.LogError("Failed to get purchased reports on refresh.", ex); } }); var orderTask = Task.Run(async() => { try { var orders = await orderService.GetMemberOrders(userId); var unCached = orders.Except(orderCache.GetAll().Select(x => x.Value).ToList(), new OrderEqualityComparer()).ToList(); orderCache.Put(orders.ToDictionary(x => x.OrderId, x => x)); var subTask = Task.Run(() => { try { DependencyService.Get <IMessagingSubscriber>().Subscribe(orders.Select(x => $"{(Device.RuntimePlatform == Device.Android ? App.TopicPrefix : "")}{x.OrderId}").ToList()); } catch { } }); var propTask = Task.Run(async() => { if (!orders.Any()) { propertyCache.Clear(); return; } var properties = await propertyService.GetProperties(unCached.Select(x => x.OrderId).ToList()); propertyCache.Update(properties); }); var imgTask = Task.Run(() => { if (!orders.Any()) { imageCache.Clear(); return; } var images = imageService.GetImages(unCached.Select(x => x.OrderId).ToList()); imageCache.Update(images); }); var subscriptionTask = Task.Run(async() => { try { // TODO: Refactor this so it can be tested. var allSubs = subService.GetSubscriptions(userId).OrderBy(x => x.StartDateTime).ToList(); var recentSub = allSubs.LastOrDefault(); var purchases = new List <InAppBillingPurchase>(); SubscriptionModel newSub = null; // Check app store purchases to see if they auto-renewed if (recentSub != null && !SubscriptionUtility.SubscriptionActive(recentSub)) { try { purchases = (await purchaseService.GetPurchases(ItemType.Subscription)).ToList(); } catch (Exception ex) { purchaseEmailLogger.LogError($"Error occurred while getting purchases.", ex); } var mostRecent = purchases.OrderBy(x => x.TransactionDateUtc)?.LastOrDefault(); if (mostRecent != null) { newSub = SubscriptionUtility.GetModelFromIAP(mostRecent, user, recentSub); if (newSub != null) { allSubs.Add(newSub); subService.AddSubscription(newSub); } } } if (!allSubs.Any()) { subCache.Clear(); } else { subCache.Put(allSubs.ToDictionary(x => x.PurchaseId, x => x)); } } catch (Exception ex) { startUpLogger.LogError("Failed to get subscriptions on refresh.", ex); } }); await Task.WhenAll(new[] { propTask, imgTask, subTask, subscriptionTask }); } catch (Exception ex) { startUpLogger.LogError($"Failed to fill caches.\n{ex.ToString()}", ex); } }); return(Task.WhenAll(new[] { prTask, orderTask })); }; var refresher = new CacheRefresher(new AnalyticsLogger <CacheRefresher>(analyticsSvc, userService), RefreshCaches); refresher.Invalidate(); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed refresher.RefreshCaches(userService.GetLoggedInAccount()); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed userService.OnLoggedIn += async(s, e) => { ClearCaches(); await refresher.RefreshCaches(e.Account); }; userService.OnLoggedOut += (s, e) => ClearCaches(); // Register services Container.Register <IOrderService>(() => orderService, Lifestyle.Singleton); Container.Register <IPropertyService>(() => propertyService, Lifestyle.Singleton); Container.Register <IImageService>(() => imageService, Lifestyle.Singleton); Container.Register <INotificationService>(() => notifyService, Lifestyle.Singleton); Container.Register <ILogger <SingleReportPurchaseViewModel> >(() => new EmailLogger <SingleReportPurchaseViewModel>(notifyService, userService), Lifestyle.Singleton); Container.RegisterConditional(typeof(ILogger <>), typeof(AnalyticsLogger <>), c => !c.Handled); Container.Register <OAuth2Authenticator>(() => authenticator, Lifestyle.Singleton); Container.Register <AccountStore>(() => AccountStore.Create(), Lifestyle.Singleton); Container.Register <ICurrentUserService>(() => userService, Lifestyle.Singleton); Container.Register <IPurchasingService>(() => purchaseService, Lifestyle.Singleton); Container.Register <ICacheRefresher>(() => refresher, Lifestyle.Singleton); Container.Register <ISubscriptionService>(() => subService, Lifestyle.Singleton); Container.Register <IOrderValidationService, OrderValidationService>(); Container.Register <IPageFactory, PageFactory>(Lifestyle.Singleton); Container.Register <IToastService>(() => DependencyService.Get <IToastService>(), Lifestyle.Singleton); Container.Register <IMessagingSubscriber>(() => DependencyService.Get <IMessagingSubscriber>(), Lifestyle.Singleton); Container.Register <IMessagingCenter>(() => MessagingCenter.Instance, Lifestyle.Singleton); Container.Register <IPurchasedReportService>(() => prService, Lifestyle.Singleton); Container.Register <IAnalyticsService>(() => analyticsSvc, Lifestyle.Singleton); Container.Register <LaunchedFromPushModel>(() => App.PushModel ?? new LaunchedFromPushModel(), Lifestyle.Singleton); // Finish registering created caches Container.Register <ICache <PropertyModel> >(() => propertyCache, Lifestyle.Singleton); Container.Register <ICache <Models.Order> >(() => orderCache, Lifestyle.Singleton); Container.Register <ICache <ImageModel> >(() => imageCache, Lifestyle.Singleton); Container.Register <ICache <SubscriptionModel> >(() => subCache, Lifestyle.Singleton); Container.Register <ICache <SettingsModel> >(() => settingsCache, Lifestyle.Singleton); Container.Register <ICache <PurchasedReportModel> >(() => prCache, Lifestyle.Singleton); Container.RegisterConditional(typeof(ICache <>), typeof(MemoryCache <>), c => !c.Handled); } catch (Exception ex) { Debug.WriteLine(ex.ToString()); throw; } }