/// <summary> /// A utility method that will pipe an Observable to an ICommand (i.e. /// it will first call its CanExecute with the provided value, then if /// the command can be executed, Execute() will be called) /// </summary> /// <param name="command">The command to be executed.</param> /// <returns>An object that when disposes, disconnects the Observable /// from the command.</returns> public static IDisposable InvokeCommand <T, TResult>(this IObservable <T> This, IReactiveCommand <TResult> command) { return(This.Throttle(x => command.CanExecuteObservable.StartWith(command.CanExecute(x)).Where(b => b)) .Select(x => command.ExecuteAsync(x).Catch(Observable.Empty <TResult>())) .Switch() .Subscribe()); }
private static async Task assertExceptionForwardedToThrownExceptions(IReactiveCommand <Unit> command, Exception exception) { var exceptions = command.ThrownExceptions.CreateCollection(); await command.ExecuteAsync() .Catch(Observable.Empty <Unit>()) .DefaultIfEmpty(Unit.Default); Assert.Equal(1, exceptions.Count); Assert.Contains(exception, exceptions); }
private static async Task assertThrowsOnExecuteAsync(IReactiveCommand <Unit> command, Exception exception) { command.ThrownExceptions.Subscribe(); var failed = false; try { await command.ExecuteAsync(); } catch (Exception ex) { failed = ex == exception; } Assert.True(failed); }
private static async Task AssertThrowsOnExecuteAsync(IReactiveCommand<Unit> command, Exception exception) { command.ThrownExceptions.Subscribe(); var failed = false; try { await command.ExecuteAsync(); } catch (Exception ex) { failed = ex == exception; } Assert.True(failed); }
private static IEnumerable <UIButton> CreateButtons(UITextView controller, IReactiveCommand <string> postImage) { var pictureImage = UIImageHelper.FromFileAuto("Images/MarkdownComposer/picture"); var linkImage = UIImageHelper.FromFileAuto("Images/MarkdownComposer/link"); var photoImage = UIImageHelper.FromFileAuto("Images/MarkdownComposer/photo"); return(new [] { CreateAccessoryButton("@", () => controller.InsertText("@")), CreateAccessoryButton("#", () => controller.InsertText("#")), CreateAccessoryButton("*", () => controller.InsertText("*")), CreateAccessoryButton("`", () => controller.InsertText("`")), CreateAccessoryButton(pictureImage, () => { var range = controller.SelectedRange; controller.InsertText("![]()"); controller.SelectedRange = new Foundation.NSRange(range.Location + 4, 0); }), CreateAccessoryButton(photoImage, () => postImage.ExecuteAsync().Catch(Observable.Empty <string>()) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(x => controller.InsertText("![Image](" + x + ")"))), CreateAccessoryButton(linkImage, () => { var range = controller.SelectedRange; controller.InsertText("[]()"); controller.SelectedRange = new Foundation.NSRange(range.Location + 1, 0); }), CreateAccessoryButton("~", () => controller.InsertText("~")), CreateAccessoryButton("=", () => controller.InsertText("=")), CreateAccessoryButton("-", () => controller.InsertText("-")), CreateAccessoryButton("+", () => controller.InsertText("+")), CreateAccessoryButton("_", () => controller.InsertText("_")), CreateAccessoryButton("[", () => controller.InsertText("[")), CreateAccessoryButton("]", () => controller.InsertText("]")), CreateAccessoryButton("<", () => controller.InsertText("<")), CreateAccessoryButton(">", () => controller.InsertText(">")), }); }
/// <summary> /// Setup the lookup logic, and use the interface to do the web call. /// </summary> /// <param name="caller"></param> public WebCallViewModel(IWebCaller caller) { // Do a search when nothing new has been entered for 800 ms and it isn't // an empty string... and don't search for the same thing twice. var newSearchNeeded = this.WhenAny(p => p.InputText, x => x.Value) .Throttle(TimeSpan.FromMilliseconds(800), RxApp.TaskpoolScheduler) .DistinctUntilChanged() .Where(x => !string.IsNullOrWhiteSpace(x)); _doWebCall = ReactiveCommand.CreateAsyncObservable(x => caller.GetResult(x as string)); newSearchNeeded.InvokeCommand(_doWebCall); // Run the web call and save the results back to the UI when done. var webResults = _doWebCall.ExecuteAsync(); // The results are stuffed into the property, on the proper thread // (ToProperty takes care of that) when done. We never want the property to // be null, so we give it an initial value of "". webResults .ToProperty(this, x => x.ResultText, out _ResultTextOAPH, ""); }
public ShellViewModel(Library library, ViewSettings viewSettings, CoreSettings coreSettings, IWindowManager windowManager, MobileApiInfo mobileApiInfo) { this.library = library; this.ViewSettings = viewSettings; this.coreSettings = coreSettings; this.disposable = new CompositeDisposable(); this.UpdateViewModel = new UpdateViewModel(viewSettings); this.library.Initialize(); this.accessToken = this.library.LocalAccessControl.RegisterLocalAccessToken(); this.library.WhenAnyValue(x => x.CurrentPlaylist).Subscribe(x => this.RaisePropertyChanged("CurrentPlaylist")); this.canChangeTime = this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockTime), this.accessToken) .ToProperty(this, x => x.CanChangeTime); this.canChangeVolume = this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockVolume), this.accessToken) .ToProperty(this, x => x.CanChangeVolume); this.canAlterPlaylist = this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlaylist), this.accessToken) .ToProperty(this, x => x.CanAlterPlaylist); this.showVotes = this.library.RemoteAccessControl.WhenAnyValue(x => x.IsGuestSystemReallyEnabled) .CombineLatest(mobileApiInfo.ConnectedClientCount, (enableGuestSystem, connectedClients) => enableGuestSystem && connectedClients > 0) .ToProperty(this, x => x.ShowVotes); mobileApiInfo.VideoPlayerToggleRequest.Subscribe(_ => this.ShowVideoPlayer = !this.ShowVideoPlayer); this.isAdmin = this.library.LocalAccessControl.ObserveAccessPermission(this.accessToken) .Select(x => x == AccessPermission.Admin) .ToProperty(this, x => x.IsAdmin); this.NextSongCommand = ReactiveCommand.CreateAsyncTask(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken) .CombineLatest(this.library.WhenAnyValue(x => x.CurrentPlaylist.CanPlayNextSong), (x1, x2) => x1 && x2) .ObserveOn(RxApp.MainThreadScheduler), _ => this.library.PlayNextSongAsync(this.accessToken)); this.PreviousSongCommand = ReactiveCommand.CreateAsyncTask(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken) .CombineLatest(this.library.WhenAnyValue(x => x.CurrentPlaylist.CanPlayPreviousSong), (x1, x2) => x1 && x2) .ObserveOn(RxApp.MainThreadScheduler), _ => this.library.PlayPreviousSongAsync(this.accessToken)); if (!this.library.Playlists.Any()) { this.library.AddAndSwitchToPlaylist(this.GetNewPlaylistName(), this.accessToken); } else { this.library.SwitchToPlaylist(this.library.Playlists.First(), this.accessToken); } this.SettingsViewModel = new SettingsViewModel(this.library, this.ViewSettings, this.coreSettings, windowManager, this.accessToken, mobileApiInfo); this.LocalViewModel = new LocalViewModel(this.library, this.ViewSettings, this.coreSettings, accessToken); this.YoutubeViewModel = new YoutubeViewModel(this.library, this.ViewSettings, this.coreSettings, accessToken); this.SoundCloudViewModel = new SoundCloudViewModel(this.library, accessToken, this.coreSettings, this.ViewSettings); this.DirectYoutubeViewModel = new DirectYoutubeViewModel(this.library, this.coreSettings, accessToken); this.currentSongSource = this.WhenAnyValue(x => x.IsLocal, x => x.IsYoutube, x => x.IsSoundCloud, (local, youtube, soundcloud) => { if (local) { return((ISongSourceViewModel)this.LocalViewModel); } if (youtube) { return(this.YoutubeViewModel); } if (soundcloud) { return(this.SoundCloudViewModel); } return(this.LocalViewModel); }) .ToProperty(this, x => x.CurrentSongSource, null, ImmediateScheduler.Instance); this.MuteCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.IsAdmin)); this.MuteCommand.Subscribe(x => this.Volume = 0); this.UnMuteCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.IsAdmin)); this.UnMuteCommand.Subscribe(x => this.Volume = 1); this.canModifyWindow = this.library.LocalAccessControl.HasAccess(this.ViewSettings.WhenAnyValue(x => x.LockWindow), this.accessToken) .ToProperty(this, x => x.CanModifyWindow); this.isPlaying = this.library.PlaybackState .Select(x => x == AudioPlayerState.Playing) .ObserveOn(RxApp.MainThreadScheduler) .ToProperty(this, x => x.IsPlaying); this.currentTime = this.library.CurrentPlaybackTime .StartWith(TimeSpan.Zero) .Select(x => x.FormatAdaptive()) .ToProperty(this, x => x.CurrentTime); this.currentSeconds = this.library.CurrentPlaybackTime .Select(x => (int)x.TotalSeconds) .ToProperty(this, x => x.CurrentSeconds); this.totalTime = this.library.TotalTime .Select(x => x.FormatAdaptive()) .ToProperty(this, x => x.TotalTime); this.totalSeconds = this.library.TotalTime .Select(x => (int)x.TotalSeconds) .ToProperty(this, x => x.TotalSeconds); this.volume = this.library.WhenAnyValue(x => x.Volume, x => (double)x) .ToProperty(this, x => x.Volume); this.AddPlaylistCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanAlterPlaylist)); this.AddPlaylistCommand.Subscribe(x => this.AddPlaylist()); this.Playlists = this.library.Playlists.CreateDerivedCollection(this.CreatePlaylistViewModel, x => x.Dispose()); this.ShowSettingsCommand = ReactiveCommand.Create(); this.ShowSettingsCommand.Subscribe(x => this.SettingsViewModel.HandleSettings()); this.ShufflePlaylistCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanAlterPlaylist)); this.ShufflePlaylistCommand.Subscribe(x => this.library.ShufflePlaylist(this.accessToken)); IObservable <bool> canPlay = this.WhenAnyValue(x => x.CurrentPlaylist.SelectedEntries) .CombineLatest(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken), this.library.LoadedSong, this.library.PlaybackState, (selectedPlaylistEntries, hasPlayAccess, loadedSong, playBackState) => // The admin can always play, but if we are in party mode, we have to check // whether it is allowed to play hasPlayAccess && // If exactly one song is selected, the command can be executed (selectedPlaylistEntries != null && selectedPlaylistEntries.Count() == 1 || // If the current song is paused, the command can be executed (loadedSong != null || playBackState == AudioPlayerState.Paused))); this.PlayCommand = ReactiveCommand.CreateAsyncTask(canPlay, async _ => { if (await this.library.PlaybackState.FirstAsync() == AudioPlayerState.Paused || await this.library.LoadedSong.FirstAsync() != null) { await this.library.ContinueSongAsync(this.accessToken); } else { await this.library.PlaySongAsync(this.CurrentPlaylist.SelectedEntries.First().Index, this.accessToken); } }); this.PlayOverrideCommand = ReactiveCommand.CreateAsyncTask(this.WhenAnyValue(x => x.CurrentPlaylist.SelectedEntries) .CombineLatest(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken), (selectedPlaylistEntries, hasAccess) => hasAccess && (selectedPlaylistEntries != null && selectedPlaylistEntries.Count() == 1)), _ => this.library.PlaySongAsync(this.CurrentPlaylist.SelectedEntries.First().Index, this.accessToken)); this.PauseCommand = ReactiveCommand.CreateAsyncTask(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken) .CombineLatest(this.WhenAnyValue(x => x.IsPlaying), (hasAccess, isPlaying) => hasAccess && isPlaying), _ => this.library.PauseSongAsync(this.accessToken)); var pauseOrContinueCommand = this.WhenAnyValue(x => x.IsPlaying) .Select(x => x ? this.PauseCommand : this.PlayCommand).Publish(null); pauseOrContinueCommand.Connect(); this.PauseContinueCommand = ReactiveCommand.CreateAsyncTask( pauseOrContinueCommand.Select(x => x.CanExecuteObservable).Switch().ObserveOn(RxApp.MainThreadScheduler), async _ => { IReactiveCommand <Unit> command = await pauseOrContinueCommand.FirstAsync(); await command.ExecuteAsync(); }); this.EditPlaylistNameCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanAlterPlaylist, x => x.CurrentPlaylist, (x1, x2) => x1 && !x2.Model.IsTemporary)); this.EditPlaylistNameCommand.Subscribe(x => this.CurrentPlaylist.EditName = true); this.RemovePlaylistCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CurrentEditedPlaylist, x => x.CurrentPlaylist, x => x.CanAlterPlaylist, (currentEditedPlaylist, currentPlaylist, canAlterPlaylist) => (currentEditedPlaylist != null || currentPlaylist != null) && canAlterPlaylist)); this.RemovePlaylistCommand.Subscribe(x => this.RemoveCurrentPlaylist()); this.IsLocal = true; }
// this was my first attempt without actually analyzing the current NavigateCommandFor source public static IReactiveCommand <object> NavigateCommandFor1 <T>(this RoutingState This, IReactiveCommand <object> navigationCommand = null, IDependencyResolver dependencyResolver = null, string contract = null) where T : IRoutableViewModel { navigationCommand = navigationCommand ?? This.Navigate; var ret = ReactiveCommand.CreateAsyncObservable(navigationCommand.CanExecuteObservable, _ => navigationCommand.ExecuteAsync((dependencyResolver ?? Locator.Current).GetService <T>(contract))); return(ret.SubscribeToCommand()); }
private static async Task assertExceptionForwardedToThrownExceptions(IReactiveCommand<Unit> command, Exception exception) { var exceptions = command.ThrownExceptions.CreateCollection(); await command.ExecuteAsync() .Catch(Observable.Empty<Unit>()) .DefaultIfEmpty(Unit.Default); Assert.Equal(1, exceptions.Count); Assert.Contains(exception, exceptions); }