private ReactiveTableViewSource <TeamItemViewModel> CreateSource(IReadOnlyReactiveList <TeamItemViewModel> items) { var source = new ReactiveTableViewSource <TeamItemViewModel>(TableView, items, TeamCellView.Key, (float)UITableView.AutomaticDimension); source.ElementSelected.OfType <TeamItemViewModel>().Subscribe(x => x.GoToCommand.ExecuteIfCan()); return(source); }
public LanguagesViewModel() { Title = "Languages"; var languages = new ReactiveList<Language>(); Languages = languages.CreateDerivedCollection( x => new LanguageItemViewModel(x.Name, x.Slug), filter: x => x.Name.StartsWith(SearchKeyword ?? string.Empty, StringComparison.OrdinalIgnoreCase), signalReset: this.WhenAnyValue(x => x.SearchKeyword)); Languages .Changed.Select(_ => Unit.Default) .Merge(this.WhenAnyValue(x => x.SelectedLanguage).Select(_ => Unit.Default)) .Select(_ => SelectedLanguage) .Where(x => x != null) .Subscribe(x => { foreach (var l in Languages) l.Selected = l.Slug == x.Slug; }); this.WhenAnyValue(x => x.SelectedLanguage) .IsNotNull() .Subscribe(_ => Dismiss()); LoadCommand = ReactiveCommand.CreateAsyncTask(async t => { var languageRepository = new LanguageRepository(); var langs = await languageRepository.GetLanguages(); langs.Insert(0, LanguageRepository.DefaultLanguage); languages.Reset(langs); }); }
public LanguagesViewModel(IJsonSerializationService jsonSerializationService, INetworkActivityService networkActivity) { var languages = new ReactiveList<LanguageModel>(); Languages = languages.CreateDerivedCollection( x => new LanguageItemViewModel(x.Name, x.Slug), x => x.Name.StartsWith(SearchKeyword ?? string.Empty, StringComparison.OrdinalIgnoreCase), signalReset: this.WhenAnyValue(x => x.SearchKeyword)); Languages .Changed.Select(_ => Unit.Default) .Merge(this.WhenAnyValue(x => x.SelectedLanguage).Select(_ => Unit.Default)) .Select(_ => SelectedLanguage) .Where(x => x != null) .Subscribe(x => { foreach (var l in Languages) l.Selected = l.Slug == x.Slug; }); LoadCommand = ReactiveCommand.CreateAsyncTask(async t => { var trendingData = await BlobCache.LocalMachine.DownloadUrl(LanguagesUrl, absoluteExpiration: DateTimeOffset.Now.AddDays(1)); var langs = jsonSerializationService.Deserialize<List<LanguageModel>>(System.Text.Encoding.UTF8.GetString(trendingData)); langs.Insert(0, DefaultLanguage); languages.Reset(langs); }); LoadCommand.TriggerNetworkActivity(networkActivity); LoadCommand.ExecuteIfCan(); }
public LanguagesViewModel(IJsonSerializationService jsonSerializationService, INetworkActivityService networkActivity) { var languages = new ReactiveList <LanguageModel>(); Languages = languages.CreateDerivedCollection( x => new LanguageItemViewModel(x.Name, x.Slug), x => x.Name.StartsWith(SearchKeyword ?? string.Empty, StringComparison.OrdinalIgnoreCase), signalReset: this.WhenAnyValue(x => x.SearchKeyword)); Languages .Changed.Select(_ => Unit.Default) .Merge(this.WhenAnyValue(x => x.SelectedLanguage).Select(_ => Unit.Default)) .Select(_ => SelectedLanguage) .Where(x => x != null) .Subscribe(x => { foreach (var l in Languages) { l.Selected = l.Slug == x.Slug; } }); LoadCommand = ReactiveCommand.CreateAsyncTask(async t => { var trendingData = await BlobCache.LocalMachine.DownloadUrl(LanguagesUrl, absoluteExpiration: DateTimeOffset.Now.AddDays(1)); var langs = jsonSerializationService.Deserialize <List <LanguageModel> >(System.Text.Encoding.UTF8.GetString(trendingData)); langs.Insert(0, DefaultLanguage); languages.Reset(langs); }); }
public ValueListNodeInputViewModel() { MaxConnections = Int32.MaxValue; ConnectionValidator = pending => new ConnectionValidationResult(pending.Output is ValueNodeOutputViewModel <T>, null); Values = Connections.ObserveLatestToList(c => ((ValueNodeOutputViewModel <T>)c.Output).WhenAnyObservable(vm => vm.Value), c => true).List; }
public static IObservable <IReadOnlyReactiveList <T> > ChangedObservable <T>(this IReadOnlyReactiveList <T> @this) { return(@this.Changed .Select(_ => Unit.Default) .StartWith(Unit.Default) .Select(_ => @this)); }
public LanguagesViewModel(LanguageRepository languageRepository) { Title = "Languages"; var languages = new ReactiveList <Language>(); Languages = languages.CreateDerivedCollection( x => new LanguageItemViewModel(x.Name, x.Slug), filter: x => x.Name.StartsWith(SearchKeyword ?? string.Empty, StringComparison.OrdinalIgnoreCase), signalReset: this.WhenAnyValue(x => x.SearchKeyword)); Languages .Changed.Select(_ => Unit.Default) .Merge(this.WhenAnyValue(x => x.SelectedLanguage).Select(_ => Unit.Default)) .Select(_ => SelectedLanguage) .Where(x => x != null) .Subscribe(x => { foreach (var l in Languages) { l.Selected = l.Slug == x.Slug; } }); LoadCommand = ReactiveCommand.CreateAsyncTask(async t => { var langs = await languageRepository.GetLanguages(); langs.Insert(0, LanguageRepository.DefaultLanguage); languages.Reset(langs); }); }
public NotificationGroupViewModel(string name, IReadOnlyReactiveList <NotificationModel> notifications, Action <NotificationGroupViewModel> readAll) : this(name, notifications) { if (readAll != null) { ReadAllCommand = ReactiveCommand.Create().WithSubscription(_ => readAll(this)); } }
public ItemsSection(string title, IReadOnlyReactiveList <ItemViewModel> items) { this.Collection = items; this.CellKeySelector = _ => cellKey; this.Header = new TableSectionHeader(title); this.SizeHint = 30; this.InitializeCellAction = x => x.Accessory = UITableViewCellAccessory.None; }
public MovieListAdapter(IReadOnlyReactiveList <IMovieModel> list, IScheduleProvider scheduleProvider) { _list = list; _inner = _list.Changed .Throttle(TimeSpan.FromMilliseconds(100)) .ObserveOn(scheduleProvider.UiScheduler) .Subscribe(_ => NotifyDataSetChanged()); HasStableIds = true; }
public SectionViewModel(string title, IReadOnlyReactiveList <ItemViewModel> items) { this.title = title; this.items = items; this.isVisible = this.items .CountChanged .Select(x => x > 0) .ToProperty(this, x => x.IsVisible, initialValue: false, scheduler: RxApp.MainThreadScheduler); }
public NotificationGroupViewModel(string name, IReadOnlyReactiveList<NotificationItemViewModel> notifications) { Notifications = notifications; Name = name; _visible = notifications.CountChanged .StartWith(notifications.Count) .Select(x => x > 0) .ToProperty(this, x => x.IsVisible); }
public ReactivePagerAdapter(IReadOnlyReactiveList <TViewModel> backingList, Func <TViewModel, ViewGroup, View> viewCreator, Action <TViewModel, View> viewInitializer = null) { this.list = backingList; this.viewCreator = viewCreator; this.viewInitializer = viewInitializer; inner = this.list.Changed.Subscribe(_ => NotifyDataSetChanged()); }
public NotificationGroupViewModel(string name, IReadOnlyReactiveList <NotificationItemViewModel> notifications) { Notifications = notifications; Name = name; _visible = notifications.CountChanged .StartWith(notifications.Count) .Select(x => x > 0) .ToProperty(this, x => x.IsVisible); }
public CommitedFilesTableViewSource(UITableView tableView, IReadOnlyReactiveList <CommitedFileItemViewModel> collection) : base(tableView, 44f) { tableView.RegisterClassForCellReuse(typeof(CommitedFileTableViewCell), CommitedFileTableViewCell.Key); collection.Changed .Select(_ => Unit.Default) .StartWith(Unit.Default) .Select(_ => collection) .Subscribe(SetData); }
public NotificationTableViewSource(UITableView tableView, IReadOnlyReactiveList <NotificationGroupViewModel> collections, Func <bool> canEdit) : base(tableView, UITableView.AutomaticDimension, 64f) { _canEdit = canEdit; tableView.RegisterNibForCellReuse(NotificationTableViewCell.Nib, NotificationTableViewCell.Key); Data = collections.CreateDerivedCollection(x => new TableSectionInformation <NotificationItemViewModel, NotificationTableViewCell>(x.Notifications, NotificationTableViewCell.Key, (float)UITableView.AutomaticDimension) { Header = new TableSectionHeader(x.Name) }, filter: x => x.IsVisible, signalReset: collections.Changed); }
public AccountsViewModel( IApplicationService applicationService = null, IAccountsService accountsService = null) { accountsService = accountsService ?? Locator.Current.GetService <IAccountsService>(); applicationService = applicationService ?? Locator.Current.GetService <IApplicationService>(); Title = "Accounts"; var activeAccount = applicationService.Account; var currentUsername = activeAccount?.Username; var accounts = new ReactiveList <Account>(); Items = accounts.CreateDerivedCollection(x => { var vm = new AccountItemViewModel(x); if (activeAccount?.Id == x.Id) { vm.GoToCommand.BindCommand(DismissCommand); vm.IsSelected = true; } else { vm.GoToCommand .Do(_ => applicationService.SetDefaultAccount(x)) .Subscribe(_ => MessageBus.Current.SendMessage(new LogoutMessage())); } vm.DeleteCommand.Subscribe(_ => { accountsService.Remove(x); accounts.Remove(x); }); return(vm); }); DismissCommand = ReactiveCommand.Create( () => { }, accounts.Changed.Select(x => accounts.Any(y => y.Username == currentUsername))); LoadCommand = ReactiveCommand.CreateFromTask(async _ => { var allAccounts = await accountsService.GetAccounts(); return(allAccounts.ToList()); }); LoadCommand.Subscribe(x => accounts.Reset(x)); }
protected UsersViewModel() { var users = new ReactiveList <UserItemViewModel>(resetChangeThreshold: 1); Items = users.CreateDerivedCollection( x => x, x => x.Username.ContainsKeyword(SearchText) || x.DisplayName.ContainsKeyword(SearchText), signalReset: this.WhenAnyValue(x => x.SearchText)); LoadCommand = ReactiveCommand.CreateFromTask(async _ => { users.Clear(); await Load(users); }); _isEmpty = LoadCommand .IsExecuting .Skip(1) .Select(x => !x && users.Count == 0) .ToProperty(this, x => x.IsEmpty); }
public IssueAssigneeViewModel( Func <Task <IReadOnlyList <User> > > loadAssignees, Func <Task <Issue> > loadIssue, Func <IssueUpdate, Task <Issue> > updateIssue ) { var derivedFunc = new Func <User, IssueAssigneeItemViewModel>(x => { var vm = new IssueAssigneeItemViewModel(x); if (_selectedUser != null) { vm.IsSelected = x.Id == _selectedUser.Id; } vm.GoToCommand.Subscribe(_ => { var assigneeName = vm.IsSelected ? vm.Name : null; updateIssue(new IssueUpdate { Assignee = assigneeName }).ToBackground(); }); return(vm); }); var assignees = new ReactiveList <User>(); Assignees = assignees.CreateDerivedCollection( derivedFunc, filter: x => x.Name.ContainsKeyword(SearchKeyword), signalReset: this.WhenAnyValue(x => x.SearchKeyword)); LoadCommand = ReactiveCommand.CreateAsyncTask(async _ => { _selectedUser = (await loadIssue()).Assignee; assignees.Reset(await loadAssignees()); }); }
public PartyViewsDragPanel(IReadOnlyReactiveList <CommunicationDiagramPartyView> partyViews) { PartyViews = partyViews; // Automatically enter label editing mode when adding a party PartyViews.OnAdd.Subscribe(elem => { if (IsVisible && (IsFocused || HasChildInFocus())) { elem.Element.LabelView.IsInEditMode = true; elem.Element.LabelView.Focus(); } }); // Two-way binding between the viewmodel and view position. partyViews.ObserveEach(partyView => partyView.ViewModel.PositionChanged) .Subscribe(e => e.Element.Position = e.Value); partyViews.ObserveEach(partyView => partyView.PositionChanged) .Subscribe(e => e.Element.ViewModel.Position = e.Value); // Automatically add and remove party views to Children. PartyViews.OnAdd.Subscribe(e => Children.Add(e.Element)); PartyViews.OnDelete.Subscribe(e => Children.Remove(e.Element)); }
this IReadOnlyReactiveList <TInput> sourceList, Func <TInput, TResult> selectorFunc, Func <TInput, bool> filterFunc = null ) { return(CreateDerivedListBinding(Observable.Return(sourceList), selectorFunc, filterFunc));
protected ReactiveRecyclerViewAdapter(IReadOnlyReactiveList <TViewModel> backingList) { this.list = backingList; _inner = this.list.Changed.Subscribe(_ => NotifyDataSetChanged()); }
public IssueGroupViewModel(string name, IEnumerable<IssueItemViewModel> issues) { Issues = new ReactiveList<IssueItemViewModel>(issues); Name = name; }
public CardAdapter(IReadOnlyReactiveList <CardViewModel> collection) : base(collection) { ItemSelected = new Subject <int>(); }
public Parent(int id) { Id = id; Children = new ReactiveList <Person>(); ChildrenReadonly = Children; }
public Parent(int id, IEnumerable <Person> children) { Id = id; Children = new ReactiveList <Person>(children); ChildrenReadonly = Children; }
public NotificationGroupViewModel(string name, IReadOnlyReactiveList <NotificationModel> notifications) { Notifications = notifications; Name = name; }
public NotificationGroupViewModel(string name, IReadOnlyReactiveList<NotificationModel> notifications, Action<NotificationGroupViewModel> readAll) : this(name, notifications) { if (readAll != null) ReadAllCommand = ReactiveCommand.Create().WithSubscription(_ => readAll(this)); }
public Parent() { Children = new ReactiveList <Person>(); ChildrenReadonly = Children; }
/// <summary> /// Clones the ReactiveList from all changes /// </summary> /// <typeparam name="TObject">The type of the object.</typeparam> /// <typeparam name="TKey">The type of the key.</typeparam> /// <param name="source">The source.</param> /// <param name="keySelector">The key selector.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">source /// or /// keySelector</exception> public static IObservable <IChangeSet <TObject, TKey> > ToObservableChangeSet <TObject, TKey>(this IReadOnlyReactiveList <TObject> source, Func <TObject, TKey> keySelector) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (keySelector == null) { throw new ArgumentNullException(nameof(keySelector)); } return(source.ToObservableChangeSet <IReadOnlyReactiveList <TObject>, TObject>().AddKey(keySelector)); }
/// <summary> /// Converts the Reactive List into an observable change set /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">The source.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">source</exception> public static IObservable <IChangeSet <T> > ToObservableChangeSet <T>(this IReadOnlyReactiveList <T> source) { return(source.ToObservableChangeSet <IReadOnlyReactiveList <T>, T>()); }
public ExerciseProgramViewModel( ILoggerService loggerService, IScheduler scheduler, IScreen hostScreen, ExerciseProgram model) { Ensure.ArgumentNotNull(loggerService, nameof(loggerService)); Ensure.ArgumentNotNull(scheduler, nameof(scheduler)); Ensure.ArgumentNotNull(hostScreen, nameof(hostScreen)); Ensure.ArgumentNotNull(model, nameof(model)); this.logger = loggerService.GetLogger(this.GetType()); this.scheduler = scheduler; this.model = model; this.hostScreen = hostScreen; this.disposables = new CompositeDisposable(); this.exercises = this.model.Exercises.CreateDerivedCollection(x => new ExerciseViewModel(scheduler, x, this.WhenAnyValue(y => y.ExecutionContext))); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.IsCancelled, (ec, isCancelled) => ec != null && !isCancelled) .ObserveOn(scheduler) .Subscribe(x => this.IsStarted = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(false) : x.WhenAnyValue(y => y.IsPaused)) .Switch() .ObserveOn(scheduler) .Subscribe(x => this.IsPaused = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(TimeSpan.Zero) : x.WhenAnyValue(y => y.Progress)) .Switch() .ObserveOn(scheduler) .Subscribe(x => this.ProgressTimeSpan = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ProgressTimeSpan) .Select(x => x.TotalMilliseconds / this.model.Duration.TotalMilliseconds) .Subscribe(x => this.Progress = x) .AddTo(this.disposables); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.CurrentExercise, (ec, currentExercise) => ec == null ? null : currentExercise) .Select(x => this.Exercises.SingleOrDefault(y => y.Model == x)) .ObserveOn(scheduler) .Subscribe(x => this.CurrentExercise = x) .AddTo(this.disposables); var canStart = this .WhenAnyValue(x => x.IsStarted) .Select(x => !x); this.startCommand = ReactiveCommand .CreateFromObservable <TimeSpan?, Unit>(this.OnStart, canStart, scheduler) .AddTo(this.disposables); var canPause = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && !isPaused) .ObserveOn(scheduler); this.pauseCommand = ReactiveCommand .CreateFromObservable(this.OnPause, canPause, scheduler) .AddTo(this.disposables); var canResume = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && isPaused) .ObserveOn(scheduler); this.resumeCommand = ReactiveCommand .CreateFromObservable(this.OnResume, canResume, scheduler) .AddTo(this.disposables); var canSkipBackwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.ProgressTimeSpan, (ec, progress) => new { ExecutionContext = ec, Progress = progress }) .Select(x => x.ExecutionContext != null && x.Progress >= skipBackwardsThreshold) .ObserveOn(scheduler); this.skipBackwardsCommand = ReactiveCommand .CreateFromObservable(this.OnSkipBackwards, canSkipBackwards, scheduler) .AddTo(this.disposables); var canSkipForwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.CurrentExercise, (ec, currentExercise) => new { ExecutionContext = ec, CurrentExercise = currentExercise }) .Select(x => x.ExecutionContext != null && x.CurrentExercise != null && x.CurrentExercise != this.exercises.LastOrDefault()) .ObserveOn(scheduler); this.skipForwardsCommand = ReactiveCommand .CreateFromObservable(this.OnSkipForwards, canSkipForwards, scheduler) .AddTo(this.disposables); this.startCommand .CanExecute .Subscribe(x => this.IsStartVisible = x) .AddTo(this.disposables); this.pauseCommand .CanExecute .Subscribe(x => this.IsPauseVisible = x) .AddTo(this.disposables); this.resumeCommand .CanExecute .Subscribe(x => this.IsResumeVisible = x) .AddTo(this.disposables); this.startCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("start", ex)) .AddTo(this.disposables); this.pauseCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("pause", ex)) .AddTo(this.disposables); this.resumeCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("resume", ex)) .AddTo(this.disposables); this.skipBackwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip backwards", ex)) .AddTo(this.disposables); this.skipForwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip forwards", ex)) .AddTo(this.disposables); // we don't use a reactive command here because switching in different commands causes it to get confused and // command binding leaves the target button disabled. We could also have not used command binding to get around // this problem this.playbackCommand = new PlaybackCommandImpl(this); // cancel the exercise program if the user navigates away this .hostScreen .Router .NavigationStack .ItemsRemoved .OfType <ExerciseProgramViewModel>() .SelectMany(x => x.Stop()) .Subscribe() .AddTo(this.disposables); }
public GroupedCollection(string name, IReadOnlyReactiveList <TViewModel> items) { Name = name; Items = items; }
public RxSpyObservablesGridViewModel(IReadOnlyReactiveList <RxSpyObservableModel> model) { Observables = model.CreateDerivedCollection(x => new RxSpyObservableGridItemViewModel(x)); }
public RxSpyObservablesGridViewModel(IReadOnlyReactiveList<RxSpyObservableModel> model) { Observables = model.CreateDerivedCollection(x => new RxSpyObservableGridItemViewModel(x)); }
public NotificationGroupViewModel(string name, IReadOnlyReactiveList<NotificationModel> notifications) { Notifications = notifications; Name = name; }
public IssueGroupViewModel(string name, IEnumerable <IssueItemViewModel> issues) { Issues = new ReactiveList <IssueItemViewModel>(issues); Name = name; }
public ExerciseProgramViewModel( ILoggerService loggerService, IScheduler scheduler, IScreen hostScreen, ExerciseProgram model) { Ensure.ArgumentNotNull(loggerService, nameof(loggerService)); Ensure.ArgumentNotNull(scheduler, nameof(scheduler)); Ensure.ArgumentNotNull(hostScreen, nameof(hostScreen)); Ensure.ArgumentNotNull(model, nameof(model)); this.logger = loggerService.GetLogger(this.GetType()); this.scheduler = scheduler; this.model = model; this.hostScreen = hostScreen; this.disposables = new CompositeDisposable(); this.exercises = this.model.Exercises.CreateDerivedCollection(x => new ExerciseViewModel(scheduler, x, this.WhenAnyValue(y => y.ExecutionContext))); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.IsCancelled, (ec, isCancelled) => ec != null && !isCancelled) .ObserveOn(scheduler) .Subscribe(x => this.IsStarted = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(false) : x.WhenAnyValue(y => y.IsPaused)) .Switch() .ObserveOn(scheduler) .Subscribe(x => this.IsPaused = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(TimeSpan.Zero) : x.WhenAnyValue(y => y.Progress)) .Switch() .ObserveOn(scheduler) .Subscribe(x => this.ProgressTimeSpan = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ProgressTimeSpan) .Select(x => x.TotalMilliseconds / this.model.Duration.TotalMilliseconds) .Subscribe(x => this.Progress = x) .AddTo(this.disposables); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.CurrentExercise, (ec, currentExercise) => ec == null ? null : currentExercise) .Select(x => this.Exercises.SingleOrDefault(y => y.Model == x)) .ObserveOn(scheduler) .Subscribe(x => this.CurrentExercise = x) .AddTo(this.disposables); var canStart = this .WhenAnyValue(x => x.IsStarted) .Select(x => !x); this.startCommand = ReactiveCommand .CreateFromObservable<TimeSpan?, Unit>(this.OnStart, canStart, scheduler) .AddTo(this.disposables); var canPause = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && !isPaused) .ObserveOn(scheduler); this.pauseCommand = ReactiveCommand .CreateFromObservable(this.OnPause, canPause, scheduler) .AddTo(this.disposables); var canResume = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && isPaused) .ObserveOn(scheduler); this.resumeCommand = ReactiveCommand .CreateFromObservable(this.OnResume, canResume, scheduler) .AddTo(this.disposables); var canSkipBackwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.ProgressTimeSpan, (ec, progress) => new { ExecutionContext = ec, Progress = progress }) .Select(x => x.ExecutionContext != null && x.Progress >= skipBackwardsThreshold) .ObserveOn(scheduler); this.skipBackwardsCommand = ReactiveCommand .CreateFromObservable(this.OnSkipBackwards, canSkipBackwards, scheduler) .AddTo(this.disposables); var canSkipForwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.CurrentExercise, (ec, currentExercise) => new { ExecutionContext = ec, CurrentExercise = currentExercise }) .Select(x => x.ExecutionContext != null && x.CurrentExercise != null && x.CurrentExercise != this.exercises.LastOrDefault()) .ObserveOn(scheduler); this.skipForwardsCommand = ReactiveCommand .CreateFromObservable(this.OnSkipForwards, canSkipForwards, scheduler) .AddTo(this.disposables); this.startCommand .CanExecute .Subscribe(x => this.IsStartVisible = x) .AddTo(this.disposables); this.pauseCommand .CanExecute .Subscribe(x => this.IsPauseVisible = x) .AddTo(this.disposables); this.resumeCommand .CanExecute .Subscribe(x => this.IsResumeVisible = x) .AddTo(this.disposables); this.startCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("start", ex)) .AddTo(this.disposables); this.pauseCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("pause", ex)) .AddTo(this.disposables); this.resumeCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("resume", ex)) .AddTo(this.disposables); this.skipBackwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip backwards", ex)) .AddTo(this.disposables); this.skipForwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip forwards", ex)) .AddTo(this.disposables); // we don't use a reactive command here because switching in different commands causes it to get confused and // command binding leaves the target button disabled. We could also have not used command binding to get around // this problem this.playbackCommand = new PlaybackCommandImpl(this); // cancel the exercise program if the user navigates away this .hostScreen .Router .NavigationStack .ItemsRemoved .OfType<ExerciseProgramViewModel>() .SelectMany(x => x.Stop()) .Subscribe() .AddTo(this.disposables); }