Example #1
0
        public MainViewModel(
            ITogglDataSource dataSource,
            ITimeService timeService,
            IRatingService ratingService,
            IUserPreferences userPreferences,
            IAnalyticsService analyticsService,
            IOnboardingStorage onboardingStorage,
            IInteractorFactory interactorFactory,
            IMvxNavigationService navigationService,
            IRemoteConfigService remoteConfigService,
            ISuggestionProviderContainer suggestionProviders,
            ISchedulerProvider schedulerProvider)
        {
            Ensure.Argument.IsNotNull(dataSource, nameof(dataSource));
            Ensure.Argument.IsNotNull(timeService, nameof(timeService));
            Ensure.Argument.IsNotNull(ratingService, nameof(ratingService));
            Ensure.Argument.IsNotNull(userPreferences, nameof(userPreferences));
            Ensure.Argument.IsNotNull(analyticsService, nameof(analyticsService));
            Ensure.Argument.IsNotNull(interactorFactory, nameof(interactorFactory));
            Ensure.Argument.IsNotNull(onboardingStorage, nameof(onboardingStorage));
            Ensure.Argument.IsNotNull(navigationService, nameof(navigationService));
            Ensure.Argument.IsNotNull(remoteConfigService, nameof(remoteConfigService));
            Ensure.Argument.IsNotNull(suggestionProviders, nameof(suggestionProviders));
            Ensure.Argument.IsNotNull(schedulerProvider, nameof(schedulerProvider));

            this.dataSource        = dataSource;
            this.timeService       = timeService;
            this.userPreferences   = userPreferences;
            this.analyticsService  = analyticsService;
            this.interactorFactory = interactorFactory;
            this.navigationService = navigationService;
            this.onboardingStorage = onboardingStorage;
            this.schedulerProvider = schedulerProvider;

            SuggestionsViewModel    = new SuggestionsViewModel(dataSource, interactorFactory, onboardingStorage, suggestionProviders);
            RatingViewModel         = new RatingViewModel(timeService, dataSource, ratingService, analyticsService, onboardingStorage, navigationService, this.schedulerProvider);
            TimeEntriesLogViewModel = new TimeEntriesLogViewModel(timeService, dataSource, interactorFactory, onboardingStorage, analyticsService, navigationService);
            timeEntriesViewModel    = new TimeEntriesViewModel(dataSource, interactorFactory);

            LogEmpty         = timeEntriesViewModel.Empty.AsDriver(this.schedulerProvider);
            TimeEntriesCount = timeEntriesViewModel.Count.AsDriver(this.schedulerProvider);

            ratingViewExperiment = new RatingViewExperiment(timeService, dataSource, onboardingStorage, remoteConfigService);

            RefreshCommand                   = new MvxCommand(Refresh);
            OpenReportsCommand               = new MvxAsyncCommand(openReports);
            OpenSettingsCommand              = new MvxAsyncCommand(openSettings);
            OpenSyncFailuresCommand          = new MvxAsyncCommand(openSyncFailures);
            EditTimeEntryCommand             = new MvxAsyncCommand(editTimeEntry, canExecuteEditTimeEntryCommand);
            StopTimeEntryCommand             = new MvxAsyncCommand(stopTimeEntry, () => isStopButtonEnabled);
            StartTimeEntryCommand            = new MvxAsyncCommand(startTimeEntry, () => CurrentTimeEntryId.HasValue == false);
            AlternativeStartTimeEntryCommand = new MvxAsyncCommand(alternativeStartTimeEntry, () => CurrentTimeEntryId.HasValue == false);

            ContinueTimeEntry = new InputAction <TimeEntryViewModel>(continueTimeEntry);
            DeleteTimeEntry   = new InputAction <TimeEntryViewModel>(deleteTimeEntry);
            SelectTimeEntry   = new InputAction <TimeEntryViewModel>(timeEntrySelected);
            RefreshAction     = new UIAction(refresh);
        }
Example #2
0
        public override async Task Initialize()
        {
            await base.Initialize();

            await TimeEntriesLogViewModel.Initialize();

            await SuggestionsViewModel.Initialize();

            var tickDisposable = timeService
                                 .CurrentDateTimeObservable
                                 .Where(_ => currentTimeEntryStart != null)
                                 .Subscribe(currentTime => CurrentTimeEntryElapsedTime = currentTime - currentTimeEntryStart.Value);

            var currentlyRunningTimeEntryDisposable = dataSource
                                                      .TimeEntries
                                                      .CurrentlyRunningTimeEntry
                                                      .Throttle(currentTimeEntryDueTime, scheduler) // avoid overwhelming the UI with frequent updates
                                                      .Subscribe(setRunningEntry);

            var syncManagerDisposable = dataSource
                                        .SyncManager
                                        .ProgressObservable
                                        .Subscribe(progress => SyncingProgress = progress);

            var isEmptyChangedDisposable = Observable.Empty <Unit>()
                                           .Merge(dataSource.TimeEntries.TimeEntryUpdated.Select(_ => Unit.Default))
                                           .Merge(dataSource.TimeEntries.TimeEntryDeleted.Select(_ => Unit.Default))
                                           .Merge(dataSource.TimeEntries.TimeEntryCreated.Select(_ => Unit.Default))
                                           .Subscribe(_ =>
            {
                RaisePropertyChanged(nameof(ShouldShowTimeEntriesLog));
                RaisePropertyChanged(nameof(ShouldShowWelcomeBack));
                RaisePropertyChanged(nameof(ShouldShowEmptyState));
                RaisePropertyChanged(nameof(IsLogEmpty));
                RaisePropertyChanged(nameof(TimeEntriesCount));
            });

            disposeBag.Add(tickDisposable);
            disposeBag.Add(syncManagerDisposable);
            disposeBag.Add(isEmptyChangedDisposable);
            disposeBag.Add(currentlyRunningTimeEntryDisposable);

            switch (urlNavigationAction)
            {
            case ApplicationUrls.Main.Action.Continue:
                await continueMostRecentEntry();

                break;

            case ApplicationUrls.Main.Action.Stop:
                await stopTimeEntry();

                break;
            }
        }
Example #3
0
        public MainViewModel(
            ITogglDataSource dataSource,
            ITimeService timeService,
            IRatingService ratingService,
            IUserPreferences userPreferences,
            IFeedbackService feedbackService,
            IAnalyticsService analyticsService,
            IOnboardingStorage onboardingStorage,
            IInteractorFactory interactorFactory,
            IMvxNavigationService navigationService,
            IRemoteConfigService remoteConfigService,
            ISuggestionProviderContainer suggestionProviders)
        {
            Ensure.Argument.IsNotNull(dataSource, nameof(dataSource));
            Ensure.Argument.IsNotNull(timeService, nameof(timeService));
            Ensure.Argument.IsNotNull(ratingService, nameof(ratingService));
            Ensure.Argument.IsNotNull(userPreferences, nameof(userPreferences));
            Ensure.Argument.IsNotNull(feedbackService, nameof(feedbackService));
            Ensure.Argument.IsNotNull(analyticsService, nameof(analyticsService));
            Ensure.Argument.IsNotNull(interactorFactory, nameof(interactorFactory));
            Ensure.Argument.IsNotNull(onboardingStorage, nameof(onboardingStorage));
            Ensure.Argument.IsNotNull(navigationService, nameof(navigationService));
            Ensure.Argument.IsNotNull(remoteConfigService, nameof(remoteConfigService));
            Ensure.Argument.IsNotNull(suggestionProviders, nameof(suggestionProviders));

            this.dataSource        = dataSource;
            this.timeService       = timeService;
            this.userPreferences   = userPreferences;
            this.analyticsService  = analyticsService;
            this.interactorFactory = interactorFactory;
            this.navigationService = navigationService;
            this.onboardingStorage = onboardingStorage;

            SuggestionsViewModel    = new SuggestionsViewModel(dataSource, interactorFactory, onboardingStorage, suggestionProviders);
            RatingViewModel         = new RatingViewModel(timeService, dataSource, ratingService, feedbackService, analyticsService, onboardingStorage, navigationService);
            TimeEntriesLogViewModel = new TimeEntriesLogViewModel(timeService, dataSource, interactorFactory, onboardingStorage, analyticsService, navigationService);

            ratingViewExperiment = new RatingViewExperiment(timeService, dataSource, onboardingStorage, remoteConfigService);

            RefreshCommand                   = new MvxCommand(refresh);
            OpenReportsCommand               = new MvxAsyncCommand(openReports);
            OpenSettingsCommand              = new MvxAsyncCommand(openSettings);
            OpenSyncFailuresCommand          = new MvxAsyncCommand(openSyncFailures);
            EditTimeEntryCommand             = new MvxAsyncCommand(editTimeEntry, () => CurrentTimeEntryId.HasValue);
            StopTimeEntryCommand             = new MvxAsyncCommand(stopTimeEntry, () => isStopButtonEnabled);
            StartTimeEntryCommand            = new MvxAsyncCommand(startTimeEntry, () => CurrentTimeEntryId.HasValue == false);
            AlternativeStartTimeEntryCommand = new MvxAsyncCommand(alternativeStartTimeEntry, () => CurrentTimeEntryId.HasValue == false);
        }
Example #4
0
        public MainViewModel(
            IScheduler scheduler,
            ITogglDataSource dataSource,
            ITimeService timeService,
            IUserPreferences userPreferences,
            IOnboardingStorage onboardingStorage,
            IInteractorFactory interactorFactory,
            IMvxNavigationService navigationService,
            ISuggestionProviderContainer suggestionProviders)
        {
            Ensure.Argument.IsNotNull(scheduler, nameof(scheduler));
            Ensure.Argument.IsNotNull(dataSource, nameof(dataSource));
            Ensure.Argument.IsNotNull(timeService, nameof(timeService));
            Ensure.Argument.IsNotNull(userPreferences, nameof(userPreferences));
            Ensure.Argument.IsNotNull(interactorFactory, nameof(interactorFactory));
            Ensure.Argument.IsNotNull(onboardingStorage, nameof(onboardingStorage));
            Ensure.Argument.IsNotNull(navigationService, nameof(navigationService));
            Ensure.Argument.IsNotNull(suggestionProviders, nameof(suggestionProviders));

            this.scheduler         = scheduler;
            this.dataSource        = dataSource;
            this.timeService       = timeService;
            this.userPreferences   = userPreferences;
            this.interactorFactory = interactorFactory;
            this.navigationService = navigationService;
            this.onboardingStorage = onboardingStorage;

            TimeEntriesLogViewModel = new TimeEntriesLogViewModel(timeService, dataSource, interactorFactory, onboardingStorage, navigationService);
            SuggestionsViewModel    = new SuggestionsViewModel(dataSource, interactorFactory, suggestionProviders);

            RefreshCommand        = new MvxCommand(refresh);
            OpenReportsCommand    = new MvxAsyncCommand(openReports);
            OpenSettingsCommand   = new MvxAsyncCommand(openSettings);
            EditTimeEntryCommand  = new MvxAsyncCommand(editTimeEntry, () => CurrentTimeEntryId.HasValue);
            StopTimeEntryCommand  = new MvxAsyncCommand(stopTimeEntry, () => isStopButtonEnabled);
            StartTimeEntryCommand = new MvxAsyncCommand(startTimeEntry, () => CurrentTimeEntryId.HasValue == false);
        }
Example #5
0
        public override async Task Initialize()
        {
            await base.Initialize();

            await TimeEntriesViewModel.Initialize();

            await TimeEntriesLogViewModel.Initialize();

            await SuggestionsViewModel.Initialize();

            await RatingViewModel.Initialize();

            SyncProgressState = dataSource.SyncManager
                                .ProgressObservable.AsDriver(schedulerProvider);

            var isWelcome = onboardingStorage.IsNewUser;

            var noTimeEntries = TimeEntriesViewModel.Empty
                                .Select(e => e && SuggestionsViewModel.IsEmpty)
                                .DistinctUntilChanged();

            ShouldShowEmptyState = ObservableAddons.CombineLatestAll(
                isWelcome,
                noTimeEntries
                )
                                   .DistinctUntilChanged()
                                   .AsDriver(schedulerProvider);

            ShouldShowWelcomeBack = ObservableAddons.CombineLatestAll(
                isWelcome.Select(b => !b),
                noTimeEntries
                )
                                    .StartWith(false)
                                    .DistinctUntilChanged()
                                    .AsDriver(schedulerProvider);

            var connectableTimeEntryIsRunning =
                dataSource
                .TimeEntries
                .CurrentlyRunningTimeEntry
                .Do(setRunningEntry)
                .Select(timeEntry => timeEntry != null)
                .DistinctUntilChanged()
                .Replay(1);

            connectableTimeEntryIsRunning.Connect();

            IsTimeEntryRunning = connectableTimeEntryIsRunning.AsDriver(schedulerProvider);

            CurrentTimeEntryHasDescription = dataSource
                                             .TimeEntries
                                             .CurrentlyRunningTimeEntry
                                             .Select(te => !string.IsNullOrWhiteSpace(te?.Description))
                                             .DistinctUntilChanged()
                                             .AsDriver(schedulerProvider);

            timeService
            .CurrentDateTimeObservable
            .Where(_ => currentTimeEntryStart != null)
            .Subscribe(currentTime => CurrentTimeEntryElapsedTime = currentTime - currentTimeEntryStart.Value)
            .DisposedBy(disposeBag);

            dataSource
            .SyncManager
            .ProgressObservable
            .Subscribe(progress => SyncingProgress = progress)
            .DisposedBy(disposeBag);

            interactorFactory
            .GetItemsThatFailedToSync()
            .Execute()
            .Select(i => i.Count())
            .Subscribe(n => NumberOfSyncFailures = n)
            .DisposedBy(disposeBag);

            timeService.MidnightObservable
            .Subscribe(onMidnight)
            .DisposedBy(disposeBag);

            switch (urlNavigationAction)
            {
            case ApplicationUrls.Main.Action.Continue:
                await continueMostRecentEntry();

                break;

            case ApplicationUrls.Main.Action.Stop:
                await stopTimeEntry();

                break;
            }

            ratingViewExperiment
            .RatingViewShouldBeVisible
            .Subscribe(presentRatingViewIfNeeded)
            .DisposedBy(disposeBag);

            onboardingStorage.StopButtonWasTappedBefore
            .Subscribe(hasBeen => hasStopButtonEverBeenUsed = hasBeen)
            .DisposedBy(disposeBag);
        }
Example #6
0
        public override async Task Initialize()
        {
            await base.Initialize();

            await TimeEntriesLogViewModel.Initialize();

            await SuggestionsViewModel.Initialize();

            await RatingViewModel.Initialize();

            var connectableTimeEntryIsRunning =
                dataSource
                .TimeEntries
                .CurrentlyRunningTimeEntry
                .Do(setRunningEntry)
                .Select(timeEntry => timeEntry != null)
                .DistinctUntilChanged()
                .Replay(1);

            connectableTimeEntryIsRunning.Connect();

            IsTimeEntryRunning = connectableTimeEntryIsRunning;

            var tickDisposable = timeService
                                 .CurrentDateTimeObservable
                                 .Where(_ => currentTimeEntryStart != null)
                                 .Subscribe(currentTime => CurrentTimeEntryElapsedTime = currentTime - currentTimeEntryStart.Value);

            var syncManagerDisposable = dataSource
                                        .SyncManager
                                        .ProgressObservable
                                        .Subscribe(progress => SyncingProgress = progress);

            var isEmptyChangedDisposable = Observable.Empty <Unit>()
                                           .Merge(dataSource.TimeEntries.Updated.Select(_ => Unit.Default))
                                           .Merge(dataSource.TimeEntries.Deleted.Select(_ => Unit.Default))
                                           .Merge(dataSource.TimeEntries.Created.Select(_ => Unit.Default))
                                           .Subscribe((Unit _) =>
            {
                RaisePropertyChanged(nameof(ShouldShowTimeEntriesLog));
                RaisePropertyChanged(nameof(ShouldShowWelcomeBack));
                RaisePropertyChanged(nameof(ShouldShowEmptyState));
                RaisePropertyChanged(nameof(IsLogEmpty));
                RaisePropertyChanged(nameof(TimeEntriesCount));
            });

            var getNumberOfSyncFailuresDisposable = interactorFactory
                                                    .GetItemsThatFailedToSync()
                                                    .Execute()
                                                    .Select(i => i.Count())
                                                    .Subscribe(n => NumberOfSyncFailures = n);

            disposeBag.Add(tickDisposable);
            disposeBag.Add(syncManagerDisposable);
            disposeBag.Add(isEmptyChangedDisposable);
            disposeBag.Add(getNumberOfSyncFailuresDisposable);

            switch (urlNavigationAction)
            {
            case ApplicationUrls.Main.Action.Continue:
                await continueMostRecentEntry();

                break;

            case ApplicationUrls.Main.Action.Stop:
                await stopTimeEntry();

                break;
            }

            ratingViewExperiment
            .RatingViewShouldBeVisible
            .Subscribe(presentRatingViewIfNeeded)
            .DisposedBy(disposeBag);

            onboardingStorage.StopButtonWasTappedBefore
            .Subscribe(hasBeen => hasStopButtonEverBeenUsed = hasBeen)
            .DisposedBy(disposeBag);
        }