public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            //Empty state button config
            EmptyStateButton.Layer.BorderWidth = 1;
            EmptyStateButton.Layer.BorderColor = Color.TimeEntriesLog.ButtonBorder.ToNativeColor().CGColor;
            EmptyStateButton.SetTitle(Resources.TimeEntriesLogEmptyStateButton, UIControlState.Normal);

            //TableView config
            var source = new TimeEntriesLogViewSource(TimeEntriesTableView);

            TimeEntriesTableView.Source = source;

            //Converters
            var visibilityConverter         = new MvxVisibilityValueConverter();
            var invertedVisibilityConverter = new MvxInvertedVisibilityValueConverter();

            var bindingSet = this.CreateBindingSet <TimeEntriesLogViewController, TimeEntriesLogViewModel>();

            //Text
            bindingSet.Bind(EmptyStateTextLabel).To(vm => vm.EmptyStateText);
            bindingSet.Bind(EmptyStateTitleLabel).To(vm => vm.EmptyStateTitle);

            //Time entries log
            bindingSet.Bind(source).To(vm => vm.TimeEntries);

            //Visibility
            bindingSet.Bind(EmptyStateView)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsEmpty)
            .WithConversion(visibilityConverter);

            bindingSet.Bind(EmptyStateButton)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsWelcome)
            .WithConversion(invertedVisibilityConverter);

            bindingSet.Bind(TimeEntriesTableView)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsEmpty)
            .WithConversion(invertedVisibilityConverter);

            bindingSet.Bind(EmptyStateImageView)
            .For(v => v.BindVisibility())
            .To($"{nameof(TimeEntriesLogViewModel.IsEmpty)}&&!{nameof(TimeEntriesLogViewModel.IsWelcome)}")
            .WithConversion(visibilityConverter);

            bindingSet.Bind(WelcomeImageView)
            .For(v => v.BindVisibility())
            .To($"{nameof(TimeEntriesLogViewModel.IsEmpty)}&&{nameof(TimeEntriesLogViewModel.IsWelcome)}")
            .WithConversion(visibilityConverter);

            //Commands
            bindingSet.Bind(source)
            .For(s => s.SelectionChangedCommand)
            .To(vm => vm.EditCommand);

            bindingSet.Apply();
        }
Beispiel #2
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            //TableView config
            var source = new TimeEntriesLogViewSource(TimeEntriesTableView);

            TimeEntriesTableView.Source = source;

            //Converters
            var visibilityConverter         = new MvxVisibilityValueConverter();
            var invertedVisibilityConverter = new MvxInvertedVisibilityValueConverter();

            var bindingSet = this.CreateBindingSet <TimeEntriesLogViewController, TimeEntriesLogViewModel>();

            //Text
            bindingSet.Bind(EmptyStateTextLabel).To(vm => vm.EmptyStateText);
            bindingSet.Bind(EmptyStateTitleLabel).To(vm => vm.EmptyStateTitle);

            //Time entries log
            bindingSet.Bind(source).To(vm => vm.TimeEntries);

            //Visibility
            bindingSet.Bind(EmptyStateView)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsEmpty)
            .WithConversion(visibilityConverter);

            bindingSet.Bind(TimeEntriesTableView)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsEmpty)
            .WithConversion(invertedVisibilityConverter);

            bindingSet.Bind(EmptyStateImageView)
            .For(v => v.BindVisibility())
            .To($"{nameof(TimeEntriesLogViewModel.IsEmpty)}&&!{nameof(TimeEntriesLogViewModel.IsWelcome)}")
            .WithConversion(visibilityConverter);

            bindingSet.Bind(WelcomeImageView)
            .For(v => v.BindVisibility())
            .To($"{nameof(TimeEntriesLogViewModel.IsEmpty)}&&{nameof(TimeEntriesLogViewModel.IsWelcome)}")
            .WithConversion(visibilityConverter);

            //Commands
            bindingSet.Bind(source)
            .For(s => s.SelectionChangedCommand)
            .To(vm => vm.EditCommand);

            bindingSet.Bind(source)
            .For(v => v.ContinueTimeEntryCommand)
            .To(vm => vm.ContinueTimeEntryCommand);

            bindingSet.Apply();
        }
Beispiel #3
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            SwipeRightBubbleLabel.Text         = Resources.SwipeRightToContinue;
            SwipeLeftBubbleLabel.Text          = Resources.SwipeLeftToDelete;
            WelcomeBackLabel.Text              = Resources.LogEmptyStateTitle;
            WelcomeBackDescriptionLabel.Text   = Resources.LogEmptyStateText;
            CreatedFirstTimeEntryLabel.Text    = Resources.YouHaveCreatedYourFirstTimeEntry;
            TapToEditItLabel.Text              = Resources.TapToEditIt;
            StartTimerBubbleLabel.Text         = Resources.TapToStartTimer;
            TapToStopTimerLabel.Text           = Resources.TapToStopTimer;
            FeedbackSentSuccessTitleLabel.Text = Resources.DoneWithExclamationMark.ToUpper();
            FeedbackSentDescriptionLabel.Text  = Resources.ThankYouForTheFeedback;

            prepareViews();
            prepareOnboarding();
            setupTableViewHeader();

            tableViewSource = new TimeEntriesLogViewSource();

            TimeEntriesLogTableView.Source = tableViewSource;

            ViewModel.TimeEntries
            .Subscribe(TimeEntriesLogTableView.Rx().AnimateSections <MainLogSection, DaySummaryViewModel, LogItemViewModel, IMainLogKey>(tableViewSource))
            .DisposedBy(disposeBag);

            ViewModel.ShouldReloadTimeEntryLog
            .WithLatestFrom(ViewModel.TimeEntries, (_, timeEntries) => timeEntries)
            .Subscribe(TimeEntriesLogTableView.Rx().ReloadSections(tableViewSource))
            .DisposedBy(disposeBag);

            tableViewSource.ToggleGroupExpansion
            .Subscribe(ViewModel.TimeEntriesViewModel.ToggleGroupExpansion.Inputs)
            .DisposedBy(disposeBag);

            tableViewSource.FirstCell
            .Subscribe(f =>
            {
                onFirstTimeEntryChanged(f);
                firstTimeEntryCell = f;
            })
            .DisposedBy(DisposeBag);

            tableViewSource.Rx().Scrolled()
            .Subscribe(onTableScroll)
            .DisposedBy(DisposeBag);

            tableViewSource.ContinueTap
            .Select(item => timeEntryContinuation(item, false))
            .Subscribe(ViewModel.ContinueTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.SwipeToContinue
            .Select(item => timeEntryContinuation(item, true))
            .Subscribe(ViewModel.ContinueTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.SwipeToDelete
            .Select(logItem => logItem.RepresentedTimeEntriesIds)
            .Subscribe(ViewModel.TimeEntriesViewModel.DelayDeleteTimeEntries.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.Rx().ModelSelected()
            .Select(editEventInfo)
            .Subscribe(ViewModel.SelectTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            ViewModel.TimeEntriesViewModel.TimeEntriesPendingDeletion
            .Subscribe(toggleUndoDeletion)
            .DisposedBy(DisposeBag);

            tableViewSource.SwipeToContinue
            .Subscribe(_ => swipeRightStep.Dismiss())
            .DisposedBy(disposeBag);

            tableViewSource.SwipeToDelete
            .Subscribe(_ => swipeLeftStep.Dismiss())
            .DisposedBy(disposeBag);

            // Refresh Control
            var refreshControl = new RefreshControl(
                ViewModel.SyncProgressState,
                tableViewSource.Rx().Scrolled(),
                tableViewSource.IsDragging);

            refreshControl.Refresh
            .Subscribe(ViewModel.Refresh.Inputs)
            .DisposedBy(DisposeBag);
            TimeEntriesLogTableView.CustomRefreshControl = refreshControl;

            //Actions
            settingsButton.Rx().BindAction(ViewModel.OpenSettings).DisposedBy(DisposeBag);
            syncFailuresButton.Rx().BindAction(ViewModel.OpenSyncFailures).DisposedBy(DisposeBag);
            StopTimeEntryButton.Rx().BindAction(ViewModel.StopTimeEntry, _ => TimeEntryStopOrigin.Manual).DisposedBy(DisposeBag);

            StartTimeEntryButton.Rx().BindAction(ViewModel.StartTimeEntry, _ => true).DisposedBy(DisposeBag);
            StartTimeEntryButton.Rx().BindAction(ViewModel.StartTimeEntry, _ => false, ButtonEventType.LongPress).DisposedBy(DisposeBag);

            CurrentTimeEntryCard.Rx().Tap()
            .WithLatestFrom(ViewModel.CurrentRunningTimeEntry, (_, te) => te)
            .Where(te => te != null)
            .Select(te => (new[] { te.Id }, EditTimeEntryOrigin.RunningTimeEntryCard))
            .Subscribe(ViewModel.SelectTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            //Visibility
            var shouldWelcomeBack = ViewModel.ShouldShowWelcomeBack;

            ViewModel.ShouldShowEmptyState
            .Subscribe(visible => emptyStateView.Hidden = !visible)
            .DisposedBy(DisposeBag);

            shouldWelcomeBack
            .Subscribe(WelcomeBackView.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            shouldWelcomeBack
            .Subscribe(spiderContainerView.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            shouldWelcomeBack
            .Subscribe(visible =>
            {
                if (visible)
                {
                    spiderBroView.Show();
                }
                else
                {
                    spiderBroView.Hide();
                }
            })
            .DisposedBy(DisposeBag);

            //Text
            ViewModel.CurrentRunningTimeEntry
            .Select(te => te?.Description)
            .Subscribe(CurrentTimeEntryDescriptionLabel.Rx().Text())
            .DisposedBy(DisposeBag);

            ViewModel.ElapsedTime
            .Subscribe(CurrentTimeEntryElapsedTimeLabel.Rx().Text())
            .DisposedBy(DisposeBag);

            var capHeight   = CurrentTimeEntryProjectTaskClientLabel.Font.CapHeight;
            var clientColor = Colors.Main.CurrentTimeEntryClientColor.ToNativeColor();

            ViewModel.CurrentRunningTimeEntry
            .Select(te => te?.ToFormattedTimeEntryString(capHeight, clientColor, shouldColorProject: true))
            .Subscribe(CurrentTimeEntryProjectTaskClientLabel.Rx().AttributedText())
            .DisposedBy(DisposeBag);

            //The start button
            var trackModeImage  = UIImage.FromBundle("playIcon");
            var manualModeImage = UIImage.FromBundle("manualIcon");

            ViewModel.IsInManualMode
            .Select(isInManualMode => isInManualMode ? manualModeImage : trackModeImage)
            .Subscribe(image => StartTimeEntryButton.SetImage(image, UIControlState.Normal))
            .DisposedBy(DisposeBag);

            //The sync failures button
            ViewModel.NumberOfSyncFailures
            .Select(numberOfSyncFailures => numberOfSyncFailures > 0)
            .Subscribe(syncFailuresButton.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            ViewModel.RatingViewModel.IsFeedbackSuccessViewShowing
            .Subscribe(SendFeedbackSuccessView.Rx().AnimatedIsVisible())
            .DisposedBy(DisposeBag);

            SendFeedbackSuccessView.Rx().Tap()
            .Subscribe(ViewModel.RatingViewModel.CloseFeedbackSuccessView)
            .DisposedBy(DisposeBag);

            ViewModel.ShouldShowRatingView
            .Subscribe(showHideRatingView)
            .DisposedBy(disposeBag);

            // Suggestion View
            suggestionsView.SuggestionTapped
            .Subscribe(ViewModel.SuggestionsViewModel.StartTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            ViewModel.SuggestionsViewModel.IsEmpty.Invert()
            .Subscribe(suggestionsView.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            ViewModel.SuggestionsViewModel.Suggestions
            .ReemitWhen(traitCollectionSubject)
            .Subscribe(suggestionsView.OnSuggestions)
            .DisposedBy(DisposeBag);

            View.SetNeedsLayout();
            View.LayoutIfNeeded();

            NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, onApplicationDidBecomeActive);
        }
Beispiel #4
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            TableShadow.Layer.MasksToBounds = false;
            TableShadow.Layer.ShadowColor   = UIColor.Black.CGColor;
            TableShadow.Layer.ShadowOffset  = new CGSize(0, 0);
            TableShadow.Layer.ShadowOpacity = 0.1f;
            TableShadow.Layer.ShadowRadius  = 4;
            TableShadow.Hidden = TraitCollection.HorizontalSizeClass != UIUserInterfaceSizeClass.Regular;

            var separator = NavigationController.NavigationBar.InsertSeparator();

            separator.BackgroundColor = ColorAssets.OpaqueSeparator;

            WelcomeBackLabel.Text              = Resources.LogEmptyStateTitle;
            WelcomeBackDescriptionLabel.Text   = Resources.LogEmptyStateText;
            CreatedFirstTimeEntryLabel.Text    = Resources.YouHaveCreatedYourFirstTimeEntry;
            TapToEditItLabel.Text              = Resources.TapToEditIt;
            StartTimerBubbleLabel.Text         = Resources.TapToStartTimer;
            TapToStopTimerLabel.Text           = Resources.TapToStopTimer;
            FeedbackSentSuccessTitleLabel.Text = Resources.DoneWithExclamationMark.ToUpper();
            FeedbackSentDescriptionLabel.Text  = Resources.ThankYouForTheFeedback;

            StartTimeEntryButton.AccessibilityLabel = Resources.StartTimeEntry;
            StopTimeEntryButton.AccessibilityLabel  = Resources.StopCurrentlyRunningTimeEntry;

            tableViewSource = new TimeEntriesLogViewSource();

            prepareViews();
            prepareOnboarding();
            setupTableViewHeader();

            ViewModel.SwipeActionsEnabled
            .Subscribe(tableViewSource.SetSwipeActionsEnabled)
            .DisposedBy(disposeBag);

            TimeEntriesLogTableView.Source          = tableViewSource;
            TimeEntriesLogTableView.BackgroundColor = ColorAssets.TableBackground;

            ViewModel.TimeEntries
            .Subscribe(TimeEntriesLogTableView.Rx().AnimateSections <MainLogSection, DaySummaryViewModel, LogItemViewModel, IMainLogKey>(tableViewSource))
            .DisposedBy(disposeBag);

            ViewModel.ShouldReloadTimeEntryLog
            .WithLatestFrom(ViewModel.TimeEntries, (_, timeEntries) => timeEntries)
            .Subscribe(TimeEntriesLogTableView.Rx().ReloadSections(tableViewSource))
            .DisposedBy(disposeBag);

            tableViewSource.ToggleGroupExpansion
            .Subscribe(ViewModel.TimeEntriesViewModel.ToggleGroupExpansion.Inputs)
            .DisposedBy(disposeBag);

            tableViewSource.FirstCell
            .Subscribe(onFirstTimeEntryChanged)
            .DisposedBy(DisposeBag);

            tableViewSource.Rx().Scrolled()
            .Subscribe(onTableScroll)
            .DisposedBy(DisposeBag);

            tableViewSource.ContinueTap
            .Select(item => timeEntryContinuation(item, false))
            .Subscribe(ViewModel.ContinueTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.SwipeToContinue
            .Select(item => timeEntryContinuation(item, true))
            .Subscribe(ViewModel.ContinueTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.SwipeToDelete
            .Select(logItem => logItem.RepresentedTimeEntriesIds)
            .Subscribe(ViewModel.TimeEntriesViewModel.DelayDeleteTimeEntries.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.Rx().ModelSelected()
            .Select(editEventInfo)
            .Subscribe(ViewModel.SelectTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            tableViewSource.Rx().ItemsChanged()
            .Subscribe(updateTooltipPositions)
            .DisposedBy(DisposeBag);

            ViewModel.TimeEntriesViewModel.TimeEntriesPendingDeletion
            .Subscribe(toggleUndoDeletion)
            .DisposedBy(DisposeBag);

            // Refresh Control
            var refreshControl = new RefreshControl(
                ViewModel.SyncProgressState,
                tableViewSource.Rx().Scrolled(),
                tableViewSource.IsDragging);

            refreshControl.Refresh
            .Subscribe(ViewModel.Refresh.Inputs)
            .DisposedBy(DisposeBag);
            TimeEntriesLogTableView.CustomRefreshControl = refreshControl;

            //Actions
            settingsButton.Rx().BindAction(ViewModel.OpenSettings).DisposedBy(DisposeBag);
            syncFailuresButton.Rx().BindAction(ViewModel.OpenSyncFailures).DisposedBy(DisposeBag);
            StopTimeEntryButton.Rx().BindAction(ViewModel.StopTimeEntry, _ => TimeEntryStopOrigin.Manual).DisposedBy(DisposeBag);

            StartTimeEntryButton.Rx().BindAction(ViewModel.StartTimeEntry, _ => true).DisposedBy(DisposeBag);
            StartTimeEntryButton.Rx().BindAction(ViewModel.StartTimeEntry, _ => false, ButtonEventType.LongPress, useFeedback: true).DisposedBy(DisposeBag);

            CurrentTimeEntryCard.Rx().Tap()
            .WithLatestFrom(ViewModel.CurrentRunningTimeEntry, (_, te) => te)
            .Where(te => te != null)
            .Select(te => new EditTimeEntryInfo(EditTimeEntryOrigin.RunningTimeEntryCard, te.Id))
            .Subscribe(ViewModel.SelectTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            //Visibility
            var shouldWelcomeBack = ViewModel.ShouldShowWelcomeBack;

            ViewModel.ShouldShowEmptyState
            .Subscribe(visible => emptyStateView.Hidden = !visible)
            .DisposedBy(DisposeBag);

            shouldWelcomeBack
            .Subscribe(WelcomeBackView.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            shouldWelcomeBack
            .Subscribe(spiderContainerView.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            shouldWelcomeBack
            .Subscribe(visible =>
            {
                if (visible)
                {
                    spiderBroView.Show();
                }
                else
                {
                    spiderBroView.Hide();
                }
            })
            .DisposedBy(DisposeBag);

            //Text
            ViewModel.CurrentRunningTimeEntry
            .Select(te => te?.Description)
            .Subscribe(CurrentTimeEntryDescriptionLabel.Rx().Text())
            .DisposedBy(DisposeBag);

            ViewModel.ElapsedTime
            .Subscribe(CurrentTimeEntryElapsedTimeLabel.Rx().Text())
            .DisposedBy(DisposeBag);

            var capHeight   = CurrentTimeEntryProjectTaskClientLabel.Font.CapHeight;
            var clientColor = ColorAssets.Text3;

            ViewModel.CurrentRunningTimeEntry
            .Select(te => te?.ToFormattedTimeEntryString(capHeight, clientColor, shouldColorProject: true))
            .Subscribe(CurrentTimeEntryProjectTaskClientLabel.Rx().AttributedText())
            .DisposedBy(DisposeBag);

            //Accessibility
            CurrentTimeEntryCard.IsAccessibilityElementFocused
            .CombineLatest(ViewModel.CurrentRunningTimeEntry,
                           (_, runningEntry) => createAccessibilityLabelForRunningEntryCard(runningEntry))
            .Subscribe(CurrentTimeEntryCard.Rx().AccessibilityLabel())
            .DisposedBy(disposeBag);

            //The start button
            var trackModeImage  = UIImage.FromBundle("playIcon");
            var manualModeImage = UIImage.FromBundle("manualIcon");

            ViewModel.IsInManualMode
            .Select(isInManualMode => isInManualMode ? manualModeImage : trackModeImage)
            .Subscribe(image => StartTimeEntryButton.SetImage(image, UIControlState.Normal))
            .DisposedBy(DisposeBag);

            //The sync failures button
            ViewModel.NumberOfSyncFailures
            .Select(numberOfSyncFailures => numberOfSyncFailures > 0)
            .Subscribe(syncFailuresButton.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            ViewModel.RatingViewModel.IsFeedbackSuccessViewShowing
            .Subscribe(SendFeedbackSuccessView.Rx().AnimatedIsVisible())
            .DisposedBy(DisposeBag);

            SendFeedbackSuccessView.Rx().Tap()
            .Subscribe(ViewModel.RatingViewModel.CloseFeedbackSuccessView)
            .DisposedBy(DisposeBag);

            ViewModel.ShouldShowRatingView
            .Subscribe(showHideRatingView)
            .DisposedBy(disposeBag);

            // Suggestion View
            suggestionsView.SuggestionTapped
            .Subscribe(ViewModel.SuggestionsViewModel.StartTimeEntry.Inputs)
            .DisposedBy(DisposeBag);

            ViewModel.SuggestionsViewModel.IsEmpty.Invert()
            .Subscribe(suggestionsView.Rx().IsVisible())
            .DisposedBy(DisposeBag);

            ViewModel.SuggestionsViewModel.Suggestions
            .ReemitWhen(traitCollectionSubject)
            .Subscribe(suggestions =>
            {
                suggestionsView.OnSuggestions(suggestions);
                layoutTableHeader();
            })
            .DisposedBy(DisposeBag);

            // Intent Donation
            IosDependencyContainer.Instance.IntentDonationService.SetDefaultShortcutSuggestions();

            Observable.Merge(
                ViewModel.ContinueTimeEntry.Elements,
                ViewModel.SuggestionsViewModel.StartTimeEntry.Elements
                )
            .Subscribe(IosDependencyContainer.Instance.IntentDonationService.DonateStartTimeEntry)
            .DisposedBy(DisposeBag);

            ViewModel.StopTimeEntry.Elements
            .Subscribe(IosDependencyContainer.Instance.IntentDonationService.DonateStopCurrentTimeEntry)
            .DisposedBy(DisposeBag);

            View.SetNeedsLayout();
            View.LayoutIfNeeded();

            NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, onApplicationDidBecomeActive);
        }
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            prepareViews();
            prepareOnboarding();
            setupTableViewHeader();

            var visibilityConverter       = new MvxVisibilityValueConverter();
            var projectTaskClientCombiner = new ProjectTaskClientValueCombiner(
                CurrentTimeEntryProjectTaskClientLabel.Font.CapHeight,
                Color.Main.CurrentTimeEntryClientColor.ToNativeColor(),
                true
                );
            var startTimeEntryButtonManualModeIconConverter = new BoolToConstantValueConverter <UIImage>(
                UIImage.FromBundle("manualIcon"),
                UIImage.FromBundle("playIcon")
                );
            var durationCombiner = new DurationValueCombiner();

            var bindingSet = this.CreateBindingSet <MainViewController, MainViewModel>();

            // Table view
            tableViewSource = new TimeEntriesLogViewSource(ViewModel.TimeEntries, TimeEntriesLogViewCell.Identifier);
            TimeEntriesLogTableView
            .Rx()
            .Bind(tableViewSource)
            .DisposedBy(disposeBag);

            this.Bind(tableViewSource.FirstCell, f =>
            {
                onFirstTimeEntryChanged(f);
                firstTimeEntryCell = f;
            });

            this.Bind(tableViewSource.ScrollOffset, onTableScroll);

            var continueTimeEntry = Observable.Merge(
                tableViewSource.ContinueTap,
                tableViewSource.SwipeToContinue
                );

            this.Bind(continueTimeEntry, ViewModel.ContinueTimeEntry);
            this.Bind(tableViewSource.SwipeToDelete, ViewModel.TimeEntriesViewModel.DelayDeleteTimeEntry);
            this.Bind(tableViewSource.ItemSelected, ViewModel.SelectTimeEntry);
            this.Bind(ViewModel.TimeEntriesViewModel.ShouldShowUndo, toggleUndoDeletion);

            tableViewSource.SwipeToContinue
            .VoidSubscribe(() =>
            {
                swipeRightStep.Dismiss();
            })
            .DisposedBy(disposeBag);

            tableViewSource.SwipeToDelete
            .VoidSubscribe(() =>
            {
                swipeLeftStep.Dismiss();
            })
            .DisposedBy(disposeBag);

            // Refresh Control
            var refreshControl = new RefreshControl(ViewModel.SyncProgressState, tableViewSource);

            this.Bind(refreshControl.Refresh, ViewModel.RefreshAction);
            TimeEntriesLogTableView.CustomRefreshControl = refreshControl;

            //Commands
            bindingSet.Bind(settingsButton).To(vm => vm.OpenSettingsCommand);
            bindingSet.Bind(StopTimeEntryButton).To(vm => vm.StopTimeEntryCommand);
            bindingSet.Bind(StartTimeEntryButton).To(vm => vm.StartTimeEntryCommand);
            bindingSet.Bind(EditTimeEntryButton).To(vm => vm.EditTimeEntryCommand);
            bindingSet.Bind(syncFailuresButton).To(vm => vm.OpenSyncFailuresCommand);

            bindingSet.Bind(CurrentTimeEntryCard)
            .For(v => v.BindTap())
            .To(vm => vm.EditTimeEntryCommand);

            bindingSet.Bind(suggestionsView)
            .For(v => v.SuggestionTappedCommad)
            .To(vm => vm.SuggestionsViewModel.StartTimeEntryCommand);

            bindingSet.Bind(StartTimeEntryButton)
            .For(v => v.BindLongPress())
            .To(vm => vm.AlternativeStartTimeEntryCommand);

            //Visibility
            var shouldWelcomeBack = ViewModel.ShouldShowWelcomeBack;

            this.Bind(ViewModel.ShouldShowEmptyState, visible => emptyStateView.Hidden = !visible);
            this.Bind(shouldWelcomeBack, WelcomeBackView.Rx().IsVisible());
            this.Bind(shouldWelcomeBack, spiderContainerView.Rx().IsVisible());
            this.Bind(shouldWelcomeBack, visible =>
            {
                if (visible)
                {
                    spiderBroView.Show();
                }
                else
                {
                    spiderBroView.Hide();
                }
            });

            //Text
            bindingSet.Bind(CurrentTimeEntryDescriptionLabel).To(vm => vm.CurrentTimeEntryDescription);

            bindingSet.Bind(CurrentTimeEntryElapsedTimeLabel)
            .ByCombining(durationCombiner,
                         vm => vm.CurrentTimeEntryElapsedTime,
                         vm => vm.CurrentTimeEntryElapsedTimeFormat);

            bindingSet.Bind(CurrentTimeEntryProjectTaskClientLabel)
            .For(v => v.AttributedText)
            .ByCombining(projectTaskClientCombiner,
                         v => v.CurrentTimeEntryProject,
                         v => v.CurrentTimeEntryTask,
                         v => v.CurrentTimeEntryClient,
                         v => v.CurrentTimeEntryProjectColor);

            //The start button
            bindingSet.Bind(StartTimeEntryButton)
            .For(v => v.BindImage())
            .To(vm => vm.IsInManualMode)
            .WithConversion(startTimeEntryButtonManualModeIconConverter);

            //The sync failures button
            bindingSet.Bind(syncFailuresButton)
            .For(v => v.BindVisibility())
            .To(vm => vm.NumberOfSyncFailures)
            .WithConversion(visibilityConverter);

            bindingSet.Apply();

            this.Bind(ViewModel.RatingViewModel.IsFeedbackSuccessViewShowing,
                      SendFeedbackSuccessView.Rx().AnimatedIsVisible());
            this.BindVoid(SendFeedbackSuccessView.Rx().Tap(), ViewModel.RatingViewModel.CloseFeedbackSuccessView);

            ViewModel.ShouldReloadTimeEntryLog
            .VoidSubscribe(reload)
            .DisposedBy(disposeBag);

            View.SetNeedsLayout();
            View.LayoutIfNeeded();

            NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, onApplicationDidBecomeActive);
        }
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            //TableView config
            var source = new TimeEntriesLogViewSource(TimeEntriesTableView);

            TimeEntriesTableView.Source = source;

            //Add negative bottom inset, so that footers won't stick to the bottom of the screen
            var bottomContentInset = -source.GetHeightForFooter(TimeEntriesTableView, 0);

            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            {
                var bottomSafeAreaInset = UIApplication.SharedApplication.KeyWindow.SafeAreaInsets.Bottom;
                bottomContentInset -= bottomSafeAreaInset;
            }
            var tableViewContentInset = TimeEntriesTableView.ContentInset;

            tableViewContentInset.Bottom      = bottomContentInset;
            TimeEntriesTableView.ContentInset = tableViewContentInset;

            //Converters
            var visibilityConverter         = new MvxVisibilityValueConverter();
            var invertedVisibilityConverter = new MvxInvertedVisibilityValueConverter();

            var bindingSet = this.CreateBindingSet <TimeEntriesLogViewController, TimeEntriesLogViewModel>();

            //Text
            bindingSet.Bind(EmptyStateTextLabel).To(vm => vm.EmptyStateText);
            bindingSet.Bind(EmptyStateTitleLabel).To(vm => vm.EmptyStateTitle);

            //Time entries log
            bindingSet.Bind(source).To(vm => vm.TimeEntries);

            //Visibility
            bindingSet.Bind(EmptyStateView)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsEmpty)
            .WithConversion(visibilityConverter);

            bindingSet.Bind(TimeEntriesTableView)
            .For(v => v.BindVisibility())
            .To(vm => vm.IsEmpty)
            .WithConversion(invertedVisibilityConverter);

            bindingSet.Bind(EmptyStateImageView)
            .For(v => v.BindVisibility())
            .To($"{nameof(TimeEntriesLogViewModel.IsEmpty)}&&!{nameof(TimeEntriesLogViewModel.IsWelcome)}")
            .WithConversion(visibilityConverter);

            bindingSet.Bind(WelcomeImageView)
            .For(v => v.BindVisibility())
            .To($"{nameof(TimeEntriesLogViewModel.IsEmpty)}&&{nameof(TimeEntriesLogViewModel.IsWelcome)}")
            .WithConversion(visibilityConverter);

            //Commands
            bindingSet.Bind(source)
            .For(s => s.SelectionChangedCommand)
            .To(vm => vm.EditCommand);

            bindingSet.Bind(source)
            .For(v => v.ContinueTimeEntryCommand)
            .To(vm => vm.ContinueTimeEntryCommand);


            bindingSet.Bind(source)
            .For(v => v.DeleteTimeEntryCommand)
            .To(vm => vm.DeleteCommand);

            bindingSet.Apply();
        }