Esempio n. 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);
        }
Esempio n. 2
0
        public MainViewModel(
            ITogglDataSource dataSource,
            ITimeService timeService,
            IRatingService ratingService,
            IUserPreferences userPreferences,
            IAnalyticsService analyticsService,
            IOnboardingStorage onboardingStorage,
            IInteractorFactory interactorFactory,
            IMvxNavigationService navigationService,
            IRemoteConfigService remoteConfigService,
            ISuggestionProviderContainer suggestionProviders,
            IIntentDonationService intentDonationService,
            IAccessRestrictionStorage accessRestrictionStorage,
            ISchedulerProvider schedulerProvider,
            IStopwatchProvider stopwatchProvider,
            IRxActionFactory rxActionFactory)
        {
            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(schedulerProvider, nameof(schedulerProvider));
            Ensure.Argument.IsNotNull(stopwatchProvider, nameof(stopwatchProvider));
            Ensure.Argument.IsNotNull(remoteConfigService, nameof(remoteConfigService));
            Ensure.Argument.IsNotNull(suggestionProviders, nameof(suggestionProviders));
            Ensure.Argument.IsNotNull(intentDonationService, nameof(intentDonationService));
            Ensure.Argument.IsNotNull(accessRestrictionStorage, nameof(accessRestrictionStorage));
            Ensure.Argument.IsNotNull(rxActionFactory, nameof(rxActionFactory));

            this.dataSource               = dataSource;
            this.userPreferences          = userPreferences;
            this.analyticsService         = analyticsService;
            this.interactorFactory        = interactorFactory;
            this.navigationService        = navigationService;
            this.onboardingStorage        = onboardingStorage;
            this.SchedulerProvider        = schedulerProvider;
            this.intentDonationService    = intentDonationService;
            this.accessRestrictionStorage = accessRestrictionStorage;
            this.stopwatchProvider        = stopwatchProvider;
            this.rxActionFactory          = rxActionFactory;

            TimeService = timeService;

            SuggestionsViewModel = new SuggestionsViewModel(dataSource, interactorFactory, onboardingStorage, suggestionProviders, schedulerProvider, rxActionFactory);
            RatingViewModel      = new RatingViewModel(timeService, dataSource, ratingService, analyticsService, onboardingStorage, navigationService, SchedulerProvider, rxActionFactory);
            TimeEntriesViewModel = new TimeEntriesViewModel(dataSource, interactorFactory, analyticsService, SchedulerProvider, rxActionFactory);

            LogEmpty         = TimeEntriesViewModel.Empty.AsDriver(SchedulerProvider);
            TimeEntriesCount = TimeEntriesViewModel.Count.AsDriver(SchedulerProvider);

            ratingViewExperiment = new RatingViewExperiment(timeService, dataSource, onboardingStorage, remoteConfigService);
        }
Esempio n. 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);
        }
Esempio n. 4
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);
        }
Esempio n. 5
0
        public override async Task Initialize()
        {
            await base.Initialize();

            await TimeEntriesViewModel.Initialize();

            await SuggestionsViewModel.Initialize();

            await RatingViewModel.Initialize();

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

            var isWelcome = onboardingStorage.IsNewUser;

            var noTimeEntries = Observable
                                .CombineLatest(TimeEntriesViewModel.Empty, SuggestionsViewModel.IsEmpty,
                                               (isTimeEntryEmpty, isSuggestionEmpty) => isTimeEntryEmpty && isSuggestionEmpty)
                                .DistinctUntilChanged();

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

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

            IsInManualMode = userPreferences
                             .IsManualModeEnabledObservable
                             .AsDriver(SchedulerProvider);

            ShouldShowRunningTimeEntryNotification = userPreferences.AreRunningTimerNotificationsEnabledObservable;
            ShouldShowStoppedTimeEntryNotification = userPreferences.AreStoppedTimerNotificationsEnabledObservable;

            CurrentRunningTimeEntry = dataSource
                                      .TimeEntries
                                      .CurrentlyRunningTimeEntry
                                      .AsDriver(SchedulerProvider);

            IsTimeEntryRunning = CurrentRunningTimeEntry
                                 .Select(te => te != null)
                                 .DistinctUntilChanged()
                                 .AsDriver(SchedulerProvider);

            var durationObservable = dataSource
                                     .Preferences
                                     .Current
                                     .Select(preferences => preferences.DurationFormat);

            ElapsedTime = TimeService
                          .CurrentDateTimeObservable
                          .CombineLatest(CurrentRunningTimeEntry, (now, te) => (now - te?.Start) ?? TimeSpan.Zero)
                          .CombineLatest(durationObservable, (duration, format) => duration.ToFormattedString(format))
                          .AsDriver(SchedulerProvider);

            NumberOfSyncFailures = interactorFactory
                                   .GetItemsThatFailedToSync()
                                   .Execute()
                                   .Select(i => i.Count())
                                   .AsDriver(SchedulerProvider);

            ShouldReloadTimeEntryLog = Observable.Merge(
                TimeService.MidnightObservable.SelectUnit(),
                TimeService.SignificantTimeChangeObservable.SelectUnit())
                                       .AsDriver(SchedulerProvider);

            Refresh           = rxActionFactory.FromAsync(refresh);
            OpenReports       = rxActionFactory.FromAsync(openReports);
            OpenSettings      = rxActionFactory.FromAsync(openSettings);
            OpenSyncFailures  = rxActionFactory.FromAsync(openSyncFailures);
            SelectTimeEntry   = rxActionFactory.FromAsync <long>(timeEntrySelected);
            DeleteTimeEntry   = rxActionFactory.FromObservable <TimeEntryViewModel>(deleteTimeEntry);
            ContinueTimeEntry = rxActionFactory.FromObservable <TimeEntryViewModel>(continueTimeEntry);
            StartTimeEntry    = rxActionFactory.FromAsync <bool>(startTimeEntry, IsTimeEntryRunning.Invert());
            StopTimeEntry     = rxActionFactory.FromAsync <TimeEntryStopOrigin>(stopTimeEntry, IsTimeEntryRunning);

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

                break;

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

                break;
            }

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

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

            interactorFactory.GetDefaultWorkspace()
            .TrackException <InvalidOperationException, IThreadSafeWorkspace>("MainViewModel.Initialize")
            .Execute()
            .Subscribe(intentDonationService.SetDefaultShortcutSuggestions)
            .DisposedBy(disposeBag);

            dataSource
            .Workspaces
            .Created
            .Subscribe(_ => onWorkspaceCreated())
            .DisposedBy(disposeBag);

            dataSource
            .Workspaces
            .Updated
            .Subscribe(onWorkspaceUpdated)
            .DisposedBy(disposeBag);
        }
Esempio n. 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);
        }