From cdf18bb7af8404c10bf4e2f36120afd3fb7f8c5c Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sat, 19 May 2012 23:19:42 -0700 Subject: [PATCH 01/12] Create a ViewModel to represent background tasks --- .../ViewModels/BackgroundTaskTileViewModel.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Play/ViewModels/BackgroundTaskTileViewModel.cs diff --git a/Play/ViewModels/BackgroundTaskTileViewModel.cs b/Play/ViewModels/BackgroundTaskTileViewModel.cs new file mode 100644 index 0000000..a6ff5b9 --- /dev/null +++ b/Play/ViewModels/BackgroundTaskTileViewModel.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Text; +using ReactiveUI; +using ReactiveUI.Xaml; + +namespace Play.ViewModels +{ + public interface IBackgroundTaskTileViewModel : IReactiveNotifyPropertyChanged + { + int CurrentProgress { get; } + string CurrentText { get; set; } + object Tag { get; set; } + } + + public class BackgroundTaskUserError : UserError + { + public BackgroundTaskUserError(string errorMessage, Exception innerException) : base(errorMessage, innerException: innerException) + { + RecoveryOptions.Add(new RecoveryCommand("Retry", _ => RecoveryOptionResult.RetryOperation)); + RecoveryOptions.Add(RecoveryCommand.Cancel); + } + } + + public class BackgroundTaskTileViewModel : ReactiveObject, IBackgroundTaskTileViewModel + { + ObservableAsPropertyHelper _CurrentProgress; + public int CurrentProgress { + get { return _CurrentProgress.Value; } + } + + string _CurrentText; + public string CurrentText { + get { return _CurrentText; } + set { this.RaiseAndSetIfChanged(x => x.CurrentText, value); } + } + + object _Tag; + public object Tag { + get { return _Tag; } + set { this.RaiseAndSetIfChanged(x => x.Tag, value); } + } + + public BackgroundTaskTileViewModel(IObservable progress) + { + progress.ToProperty(this, x => x.CurrentProgress); + } + + public static IBackgroundTaskTileViewModel Create(ReactiveCollection collection, IObservable progress, string captionText) + { + var prg = progress.Multicast(new Subject()); + + var ret = new BackgroundTaskTileViewModel(prg) {CurrentText = captionText}; + + prg.ObserveOn(RxApp.DeferredScheduler).Subscribe( + x => { }, + ex => { collection.Remove(ret); UserError.Throw(new BackgroundTaskUserError("Transfer Failed", ex)); }, + () => collection.Remove(ret)); + prg.Connect(); + + collection.Add(ret); + return ret; + } + } +} From 0032b8dfe81a10cc11febdf55c985c50bc4976df Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sun, 20 May 2012 00:11:44 -0700 Subject: [PATCH 02/12] Add the Download methods to Play API --- Play.Tests/Models/PlayApiTests.cs | 20 +++++++++++++++++- Play/Models/PlayApi.cs | 34 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/Play.Tests/Models/PlayApiTests.cs b/Play.Tests/Models/PlayApiTests.cs index fd64c8e..5591a22 100644 --- a/Play.Tests/Models/PlayApiTests.cs +++ b/Play.Tests/Models/PlayApiTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reactive.Linq; using System.Text; @@ -112,5 +113,22 @@ public void MakeAlbumSearchIntegrationTest() result.Count.Should().BeGreaterThan(2); } + [Fact(Skip = "Only enable this to test downloads")] + public void DownloadAlbumIntegrationTest() + { + var kernel = new MoqMockingKernel(); + var client = new RestClient(IntegrationTestUrl.Current); + + client.AddDefaultHeader("Authorization", IntegrationTestUrl.Token); + kernel.Bind().To(); + + var api = new PlayApi(client, kernel.Get()); + + var result = api.DownloadAlbum("Beirut", "Lon Gisland EP").First(); + + using (var of = File.OpenWrite("C:\\Users\\Administrator\\" + result.Item1)) { + new MemoryStream(result.Item2).CopyTo(of); + } + } } -} +} \ No newline at end of file diff --git a/Play/Models/PlayApi.cs b/Play/Models/PlayApi.cs index c7ecc15..9574abf 100644 --- a/Play/Models/PlayApi.cs +++ b/Play/Models/PlayApi.cs @@ -5,6 +5,7 @@ using System.Reactive; using System.Reactive.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Media.Imaging; using Akavache; @@ -28,6 +29,8 @@ public interface IPlayApi IObservable> Search(string query); IObservable> AllSongsForArtist(string name); IObservable> AllSongsOnAlbum(string artist, string album); + IObservable> DownloadSong(Song song); + IObservable> DownloadAlbum(string artist, string album); IObservable ConnectToSongChangeNotifications(); } @@ -124,6 +127,24 @@ public IObservable> AllSongsOnAlbum(string artist, string album) return client.RequestAsync(rq).Select(x => x.Data.songs); } + public IObservable> DownloadSong(Song song) + { + var rq = new RestRequest(String.Format("song/{0}/download", song.id)); + + return client.RequestAsync(rq) + .Select(x => Tuple.Create(fileNameFromResponse(x) ?? "song.mp3", x.RawBytes)); + } + + public IObservable> DownloadAlbum(string artist, string album) + { + var rq = new RestRequest(String.Format("artist/{0}/album/{1}/download", + HttpUtility.UrlEncode(artist).Replace("+", "%20"), + HttpUtility.UrlEncode(album).Replace("+", "%20"))); + + return client.RequestAsync(rq) + .Select(x => Tuple.Create(fileNameFromResponse(x) ?? "songs.zip", x.RawBytes)); + } + public IObservable ConnectToSongChangeNotifications() { var rq = new RestRequest("streaming_info"); @@ -138,5 +159,18 @@ public IObservable ListenUrl() var rq = new RestRequest("streaming_info"); return client.RequestAsync(rq).Select(x => x.Data.stream_url); } + + string fileNameFromResponse(RestResponse response) + { + var disp = response.Headers.FirstOrDefault(y => y.Name.Equals("content-disposition", StringComparison.InvariantCultureIgnoreCase)); + if (disp == null) { + return null; + } + + var re = new Regex("filename=\"([^\"]+)\"(;|$)"); + var m = re.Match((string) disp.Value); + + return m.Success ? m.Groups[1].Value : null; + } } } From 594e49689987b28419b1beb0feb56a6b220af068 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sun, 20 May 2012 00:28:30 -0700 Subject: [PATCH 03/12] Initial hack at handling song/album once they arrive --- Play/Play.csproj | 1 + Play/ViewModels/SongTileViewModel.cs | 52 ++++++++++++++++++++++++++++ Play/packages.config | 1 + 3 files changed, 54 insertions(+) diff --git a/Play/Play.csproj b/Play/Play.csproj index b83c134..8aeb491 100644 --- a/Play/Play.csproj +++ b/Play/Play.csproj @@ -174,6 +174,7 @@ + diff --git a/Play/ViewModels/SongTileViewModel.cs b/Play/ViewModels/SongTileViewModel.cs index 4ab8482..4f68fce 100644 --- a/Play/ViewModels/SongTileViewModel.cs +++ b/Play/ViewModels/SongTileViewModel.cs @@ -1,8 +1,11 @@ using System; +using System.IO; +using System.Linq; using System.Reactive; using System.Reactive.Linq; using System.Windows; using System.Windows.Media.Imaging; +using ICSharpCode.SharpZipLib.Zip; using Play.Models; using ReactiveUI; using ReactiveUI.Xaml; @@ -19,6 +22,9 @@ public interface ISongTileViewModel : IReactiveNotifyPropertyChanged ReactiveAsyncCommand QueueSong { get; } ReactiveAsyncCommand QueueAlbum { get; } + ReactiveCommand DownloadSong { get; } + ReactiveCommand DownloadAlbum { get; } + ReactiveAsyncCommand ShowSongsFromArtist { get; } ReactiveAsyncCommand ShowSongsFromAlbum { get; } @@ -49,6 +55,9 @@ public Visibility QueueSongVisibility { public ReactiveAsyncCommand QueueSong { get; protected set; } public ReactiveAsyncCommand QueueAlbum { get; protected set; } + public ReactiveCommand DownloadSong { get; protected set; } + public ReactiveCommand DownloadAlbum { get; protected set; } + public ReactiveAsyncCommand ShowSongsFromArtist { get; protected set; } public ReactiveAsyncCommand ShowSongsFromAlbum { get; protected set; } @@ -79,6 +88,10 @@ public SongTileViewModel(Song model, IPlayApi playApi) QueueAlbum.ThrownExceptions.Subscribe(x => { }); + DownloadAlbum = new ReactiveCommand(); + + DownloadSong = new ReactiveCommand(); + IsStarred = model.starred; ToggleStarred = new ReactiveAsyncCommand(); @@ -97,5 +110,44 @@ IObservable reallyTryToQueueSong(IPlayApi playApi, Song song) .Timeout(TimeSpan.FromSeconds(20), RxApp.TaskpoolScheduler) .Retry(3); } + + IObservable postProcessSong(Tuple fileAndData, string rootPath = null) + { + rootPath = rootPath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyMusic); + var paths = new[] { + Model.artist, Model.album + }; + + return Observable.Start(() => { + var targetDir = paths.Aggregate(new DirectoryInfo(rootPath), (acc, x) => { + if (!acc.Exists) { + acc.Create(); + } + return new DirectoryInfo(Path.Combine(acc.FullName, x)); + }); + + File.WriteAllBytes(Path.Combine(targetDir.FullName, fileAndData.Item1), fileAndData.Item2); + }, RxApp.TaskpoolScheduler); + } + + IObservable postProcessAlbum(Tuple fileAndData, string rootPath = null) + { + rootPath = rootPath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyMusic); + var paths = new[] { + Model.artist, Model.album + }; + + return Observable.Start(() => { + var targetDir = paths.Aggregate(new DirectoryInfo(rootPath), (acc, x) => { + if (!acc.Exists) { + acc.Create(); + } + return new DirectoryInfo(Path.Combine(acc.FullName, x)); + }); + + var zip = new FastZip(); + zip.ExtractZip(new MemoryStream(fileAndData.Item2), targetDir.FullName, FastZip.Overwrite.Never, null, null, null, true, true); + }, RxApp.TaskpoolScheduler); + } } } \ No newline at end of file diff --git a/Play/packages.config b/Play/packages.config index ee69bc0..909c21c 100644 --- a/Play/packages.config +++ b/Play/packages.config @@ -9,4 +9,5 @@ + \ No newline at end of file From eb089c5208e62547109f94a53566f81dd0749603 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sun, 20 May 2012 00:39:54 -0700 Subject: [PATCH 04/12] Add cancellation to background tasks --- .../ViewModels/BackgroundTaskTileViewModel.cs | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/Play/ViewModels/BackgroundTaskTileViewModel.cs b/Play/ViewModels/BackgroundTaskTileViewModel.cs index a6ff5b9..828f48f 100644 --- a/Play/ViewModels/BackgroundTaskTileViewModel.cs +++ b/Play/ViewModels/BackgroundTaskTileViewModel.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Text; +using System.Threading; using ReactiveUI; using ReactiveUI.Xaml; @@ -14,6 +16,8 @@ public interface IBackgroundTaskTileViewModel : IReactiveNotifyPropertyChanged int CurrentProgress { get; } string CurrentText { get; set; } object Tag { get; set; } + + ReactiveCommand Cancel { get; } } public class BackgroundTaskUserError : UserError @@ -44,12 +48,15 @@ public object Tag { set { this.RaiseAndSetIfChanged(x => x.Tag, value); } } + public ReactiveCommand Cancel { get; protected set; } + public BackgroundTaskTileViewModel(IObservable progress) { progress.ToProperty(this, x => x.CurrentProgress); + Cancel = new ReactiveCommand(); } - public static IBackgroundTaskTileViewModel Create(ReactiveCollection collection, IObservable progress, string captionText) + public static DisposableContainer Create(ReactiveCollection collection, IObservable progress, string captionText, IDisposable workSubscription) { var prg = progress.Multicast(new Subject()); @@ -59,10 +66,46 @@ public static IBackgroundTaskTileViewModel Create(ReactiveCollection { }, ex => { collection.Remove(ret); UserError.Throw(new BackgroundTaskUserError("Transfer Failed", ex)); }, () => collection.Remove(ret)); - prg.Connect(); + + var disp = prg.Connect(); collection.Add(ret); - return ret; + + var container = DisposableContainer.Create((IBackgroundTaskTileViewModel)ret, + new CompositeDisposable(disp, workSubscription ?? Disposable.Empty)); + + ret.Cancel.Subscribe(_ => container.Dispose()); + + return container; + } + } + + public static class DisposableContainer + { + public static DisposableContainer Create(T1 value, IDisposable disposable) + { + return new DisposableContainer(value, disposable); + } + } + + public sealed class DisposableContainer : IDisposable + { + IDisposable _inner; + + public T Value { get; private set; } + + public DisposableContainer(T value, IDisposable disposable) + { + Value = value; + _inner = disposable; + } + + public void Dispose() + { + var disp = Interlocked.Exchange(ref _inner, null); + if (disp != null) { + disp.Dispose(); + } } } } From 81a507567f74360a0eea32cb25a6f611ab6826bd Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sun, 20 May 2012 01:04:15 -0700 Subject: [PATCH 05/12] Create a BackgroundTasksHost interface --- Play/ViewModels/BackgroundTaskTileViewModel.cs | 4 +++- Play/ViewModels/PlayViewModel.cs | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Play/ViewModels/BackgroundTaskTileViewModel.cs b/Play/ViewModels/BackgroundTaskTileViewModel.cs index 828f48f..5278f54 100644 --- a/Play/ViewModels/BackgroundTaskTileViewModel.cs +++ b/Play/ViewModels/BackgroundTaskTileViewModel.cs @@ -56,12 +56,14 @@ public BackgroundTaskTileViewModel(IObservable progress) Cancel = new ReactiveCommand(); } - public static DisposableContainer Create(ReactiveCollection collection, IObservable progress, string captionText, IDisposable workSubscription) + public static DisposableContainer Create(IObservable progress, string captionText, IDisposable workSubscription) { var prg = progress.Multicast(new Subject()); var ret = new BackgroundTaskTileViewModel(prg) {CurrentText = captionText}; + var collection = RxApp.GetService().BackgroundTasks; + prg.ObserveOn(RxApp.DeferredScheduler).Subscribe( x => { }, ex => { collection.Remove(ret); UserError.Throw(new BackgroundTaskUserError("Transfer Failed", ex)); }, diff --git a/Play/ViewModels/PlayViewModel.cs b/Play/ViewModels/PlayViewModel.cs index 53711dd..6c56cbe 100644 --- a/Play/ViewModels/PlayViewModel.cs +++ b/Play/ViewModels/PlayViewModel.cs @@ -18,7 +18,12 @@ namespace Play.ViewModels { - public interface IPlayViewModel : IRoutableViewModel + public interface IBackgroundTaskHostViewModel : IReactiveNotifyPropertyChanged + { + ReactiveCollection BackgroundTasks { get; } + } + + public interface IPlayViewModel : IRoutableViewModel, IBackgroundTaskHostViewModel { IPlayApi AuthenticatedClient { get; } string ListenUrl { get; } @@ -70,6 +75,8 @@ public bool IsPlaying { set { this.RaiseAndSetIfChanged(x => x.IsPlaying, value); } } + public ReactiveCollection BackgroundTasks { get; protected set; } + public ReactiveCommand TogglePlay { get; protected set; } public ReactiveCommand Search { get; protected set; } public ReactiveCommand Logout { get; protected set; } @@ -78,6 +85,7 @@ public bool IsPlaying { public PlayViewModel(IScreen screen, ILoginMethods loginMethods) { HostScreen = screen; + BackgroundTasks = new ReactiveCollection(); TogglePlay = new ReactiveCommand(); Logout = new ReactiveCommand(); Search = new ReactiveCommand(); From 5b5145f58c4dd5d90a1132ef20bcd5d748cb5af3 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sun, 20 May 2012 01:05:44 -0700 Subject: [PATCH 06/12] Initial hack at actually queuing downloads Right now RestSharp doesn't provide any progress on its downloads, so we fake it by just using a timer. It's not ideal. --- Play/ViewModels/SongTileViewModel.cs | 29 ++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Play/ViewModels/SongTileViewModel.cs b/Play/ViewModels/SongTileViewModel.cs index 4f68fce..e71ff14 100644 --- a/Play/ViewModels/SongTileViewModel.cs +++ b/Play/ViewModels/SongTileViewModel.cs @@ -3,9 +3,11 @@ using System.Linq; using System.Reactive; using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Windows; using System.Windows.Media.Imaging; using ICSharpCode.SharpZipLib.Zip; +using Ninject; using Play.Models; using ReactiveUI; using ReactiveUI.Xaml; @@ -63,7 +65,7 @@ public Visibility QueueSongVisibility { public ReactiveAsyncCommand ToggleStarred { get; protected set; } - public SongTileViewModel(Song model, IPlayApi playApi) + public SongTileViewModel(Song model, IPlayApi playApi, [Optional][Named("MusicDir")] string musicDir) { Model = model; @@ -89,9 +91,32 @@ public SongTileViewModel(Song model, IPlayApi playApi) QueueAlbum.ThrownExceptions.Subscribe(x => { }); DownloadAlbum = new ReactiveCommand(); - DownloadSong = new ReactiveCommand(); + DownloadAlbum.Subscribe(_ => { + var dl = playApi.DownloadAlbum(Model.artist, model.album) + .SelectMany(x => postProcessAlbum(x, musicDir)) + .Multicast(new Subject()); + + var timer = Observable.Timer(DateTimeOffset.MinValue, TimeSpan.FromSeconds(3.0)) + .Select(x => (int)x * 5) + .Where(x => x < 100).TakeUntil(dl); + + BackgroundTaskTileViewModel.Create(timer, "Downloading " + Model.album, dl.Connect()); + }); + + DownloadSong.Subscribe(_ => { + var dl = playApi.DownloadSong(Model) + .SelectMany(x => postProcessSong(x, musicDir)) + .Multicast(new Subject()); + + var timer = Observable.Timer(DateTimeOffset.MinValue, TimeSpan.FromSeconds(3.0)) + .Select(x => (int)x * 5) + .Where(x => x < 100).TakeUntil(dl); + + BackgroundTaskTileViewModel.Create(timer, "Downloading " + Model.name, dl.Connect()); + }); + IsStarred = model.starred; ToggleStarred = new ReactiveAsyncCommand(); From 35183bdbbabd5655fcf52cc0cd9d7071b27fc4a2 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sun, 20 May 2012 01:35:14 -0700 Subject: [PATCH 07/12] Woot! Both song and album downloads work! --- Play/Play.csproj | 3 +++ Play/ViewModels/AppBootstrapper.cs | 1 + Play/ViewModels/SongTileViewModel.cs | 19 ++++++++++++++++--- Play/Views/SongTileView.xaml | 4 ++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Play/Play.csproj b/Play/Play.csproj index 8aeb491..955556f 100644 --- a/Play/Play.csproj +++ b/Play/Play.csproj @@ -80,6 +80,9 @@ ..\packages\Hardcodet.Wpf.TaskbarNotification.1.0.4.0\lib\net40\Hardcodet.Wpf.TaskbarNotification.dll + + ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + ..\packages\Microsoft.CompilerServices.AsyncTargetingPack.1.0.0\lib\net40\Microsoft.CompilerServices.AsyncTargetingPack.Net4.dll diff --git a/Play/ViewModels/AppBootstrapper.cs b/Play/ViewModels/AppBootstrapper.cs index 6e965b1..8f7b0fc 100644 --- a/Play/ViewModels/AppBootstrapper.cs +++ b/Play/ViewModels/AppBootstrapper.cs @@ -118,6 +118,7 @@ IKernel createDefaultKernel() ret.Bind().ToConstant(this); ret.Bind().To(); ret.Bind().To(); + ret.Bind().To(); ret.Bind().To(); ret.Bind>().To(); ret.Bind>().To(); diff --git a/Play/ViewModels/SongTileViewModel.cs b/Play/ViewModels/SongTileViewModel.cs index e71ff14..c1b741e 100644 --- a/Play/ViewModels/SongTileViewModel.cs +++ b/Play/ViewModels/SongTileViewModel.cs @@ -6,6 +6,7 @@ using System.Reactive.Subjects; using System.Windows; using System.Windows.Media.Imaging; +using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; using Ninject; using Play.Models; @@ -65,7 +66,7 @@ public Visibility QueueSongVisibility { public ReactiveAsyncCommand ToggleStarred { get; protected set; } - public SongTileViewModel(Song model, IPlayApi playApi, [Optional][Named("MusicDir")] string musicDir) + public SongTileViewModel(Song model, IPlayApi playApi, [Optional][Named("MusicDir")] string musicDir = null) { Model = model; @@ -151,6 +152,8 @@ IObservable postProcessSong(Tuple fileAndData, string root return new DirectoryInfo(Path.Combine(acc.FullName, x)); }); + if (!targetDir.Exists) targetDir.Create(); + File.WriteAllBytes(Path.Combine(targetDir.FullName, fileAndData.Item1), fileAndData.Item2); }, RxApp.TaskpoolScheduler); } @@ -159,7 +162,7 @@ IObservable postProcessAlbum(Tuple fileAndData, string roo { rootPath = rootPath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyMusic); var paths = new[] { - Model.artist, Model.album + Model.artist, // NB: Zip folder itself has Album name }; return Observable.Start(() => { @@ -167,11 +170,21 @@ IObservable postProcessAlbum(Tuple fileAndData, string roo if (!acc.Exists) { acc.Create(); } + return new DirectoryInfo(Path.Combine(acc.FullName, x)); }); + + if (!targetDir.Exists) targetDir.Create(); + + // NB: https://github.com/play/play/issues/169 + using (var archive = TarArchive.CreateInputTarArchive(new MemoryStream(fileAndData.Item2))) { + archive.ExtractContents(targetDir.FullName); + } + /* var zip = new FastZip(); - zip.ExtractZip(new MemoryStream(fileAndData.Item2), targetDir.FullName, FastZip.Overwrite.Never, null, null, null, true, true); + zip.ExtractZip(new MemoryStream(fileAndData.Item2), targetDir.FullName, FastZip.Overwrite.Always, null, null, null, true, true); + */ }, RxApp.TaskpoolScheduler); } } diff --git a/Play/Views/SongTileView.xaml b/Play/Views/SongTileView.xaml index 1ab3920..12f3226 100644 --- a/Play/Views/SongTileView.xaml +++ b/Play/Views/SongTileView.xaml @@ -73,7 +73,7 @@ - + @@ -94,7 +94,7 @@ - + From 5da0a841f3d40d11fe718192cd8b1d8ab05239d7 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Fri, 6 Jul 2012 19:27:53 -0700 Subject: [PATCH 08/12] Upgrade to released RxUI 3.2 --- Play.Tests/Play.Tests.csproj | 32 ++++++++++-------- Play.Tests/packages.config | 6 ++++ Play/Play.csproj | 33 ++++++++++++------- .../ViewModels/BackgroundTaskTileViewModel.cs | 1 - Play/packages.config | 4 +++ 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/Play.Tests/Play.Tests.csproj b/Play.Tests/Play.Tests.csproj index d5b0a03..1ce6bb4 100644 --- a/Play.Tests/Play.Tests.csproj +++ b/Play.Tests/Play.Tests.csproj @@ -43,9 +43,9 @@ ..\packages\Microsoft.CompilerServices.AsyncTargetingPack.1.0.0\lib\net40\Microsoft.CompilerServices.AsyncTargetingPack.Net4.dll - False - ..\ext\Microsoft.Reactive.Testing.dll + ..\packages\Rx_Experimental-Testing.1.1.11111\lib\Net4-Full\Microsoft.Reactive.Testing.dll + ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll True @@ -74,18 +74,24 @@ ..\ext\PusherClientDotNet.dll - - ..\ext\ReactiveUI.dll + + False + ..\packages\reactiveui-core.3.2.0\lib\Net4\ReactiveUI.dll - - ..\ext\ReactiveUI.Routing.dll + + ..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Blend.dll - - ..\ext\ReactiveUI.Testing.dll + + False + ..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Routing.dll - + False - ..\ext\ReactiveUI.Xaml.dll + ..\packages\reactiveui-testing.3.2.0\lib\Net4\ReactiveUI.Testing.dll + + + False + ..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Xaml.dll ..\packages\RestSharp.103.0.0-nojsondotnet\lib\net35\RestSharp.dll @@ -94,12 +100,10 @@ - False - ..\ext\System.Reactive.dll + ..\packages\Rx_Experimental-Main.1.1.11111\lib\Net4\System.Reactive.dll - False - ..\ext\System.Reactive.Windows.Threading.dll + ..\packages\Rx_Experimental-Xaml.1.1.11111\lib\Net4\System.Reactive.Windows.Threading.dll diff --git a/Play.Tests/packages.config b/Play.Tests/packages.config index 20dfd60..48b8d0d 100644 --- a/Play.Tests/packages.config +++ b/Play.Tests/packages.config @@ -7,7 +7,13 @@ + + + + + + \ No newline at end of file diff --git a/Play/Play.csproj b/Play/Play.csproj index 955556f..039b638 100644 --- a/Play/Play.csproj +++ b/Play/Play.csproj @@ -114,17 +114,21 @@ ..\ext\PusherClientDotNet.dll - - ..\ext\ReactiveUI.dll + + False + ..\packages\reactiveui-core.3.2.0\lib\Net4\ReactiveUI.dll - - ..\ext\ReactiveUI.Blend.dll + + False + ..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Blend.dll - - ..\ext\ReactiveUI.Routing.dll + + False + ..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Routing.dll - - ..\ext\ReactiveUI.Xaml.dll + + False + ..\packages\reactiveui-xaml.3.2.0\lib\Net4\ReactiveUI.Xaml.dll ..\packages\RestSharp.103.0.0-nojsondotnet\lib\net35\RestSharp.dll @@ -140,12 +144,10 @@ - False - ..\ext\System.Reactive.dll + ..\packages\Rx_Experimental-Main.1.1.11111\lib\Net4\System.Reactive.dll - False - ..\ext\System.Reactive.Windows.Threading.dll + ..\packages\Rx_Experimental-Xaml.1.1.11111\lib\Net4\System.Reactive.Windows.Threading.dll @@ -182,6 +184,9 @@ + + Dummy.xaml + SearchView.xaml @@ -210,6 +215,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/Play/ViewModels/BackgroundTaskTileViewModel.cs b/Play/ViewModels/BackgroundTaskTileViewModel.cs index 5278f54..f36aea9 100644 --- a/Play/ViewModels/BackgroundTaskTileViewModel.cs +++ b/Play/ViewModels/BackgroundTaskTileViewModel.cs @@ -59,7 +59,6 @@ public BackgroundTaskTileViewModel(IObservable progress) public static DisposableContainer Create(IObservable progress, string captionText, IDisposable workSubscription) { var prg = progress.Multicast(new Subject()); - var ret = new BackgroundTaskTileViewModel(prg) {CurrentText = captionText}; var collection = RxApp.GetService().BackgroundTasks; diff --git a/Play/packages.config b/Play/packages.config index 909c21c..72d7add 100644 --- a/Play/packages.config +++ b/Play/packages.config @@ -8,6 +8,10 @@ + + + + \ No newline at end of file From 9980033daac4fe7dc33d1f13f32c79b695c1c763 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Fri, 6 Jul 2012 22:18:35 -0700 Subject: [PATCH 09/12] Add some initial Views for background tasks --- Play/Images/carbon_fibre_big.png | Bin 0 -> 1313 bytes Play/Play.csproj | 24 ++++++++ Play/Properties/DesignTimeResources.xaml | 8 +++ Play/Views/BackgroundTaskHostView.xaml | 35 +++++++++++ Play/Views/BackgroundTaskHostView.xaml.cs | 37 ++++++++++++ Play/Views/BackgroundTaskTileView.xaml | 17 ++++++ Play/Views/BackgroundTaskTileView.xaml.cs | 26 ++++++++ Play/Views/Dummy.xaml | 69 ++++++++++++++++++++++ Play/Views/Dummy.xaml.cs | 27 +++++++++ 9 files changed, 243 insertions(+) create mode 100644 Play/Images/carbon_fibre_big.png create mode 100644 Play/Properties/DesignTimeResources.xaml create mode 100644 Play/Views/BackgroundTaskHostView.xaml create mode 100644 Play/Views/BackgroundTaskHostView.xaml.cs create mode 100644 Play/Views/BackgroundTaskTileView.xaml create mode 100644 Play/Views/BackgroundTaskTileView.xaml.cs create mode 100644 Play/Views/Dummy.xaml create mode 100644 Play/Views/Dummy.xaml.cs diff --git a/Play/Images/carbon_fibre_big.png b/Play/Images/carbon_fibre_big.png new file mode 100644 index 0000000000000000000000000000000000000000..b693b37fc8c6e9267740c489059a8401b7b37914 GIT binary patch literal 1313 zcmah}Uu+ab7+*^;<^r*a0%A|&fH`D+^E?{Cpffq1=Cj~7&7>tq<^g(B@y#pf`H@iFE zOn%?*`#dNL#k!shItp5yD|{M-6S8~?xDxnnn9wzJF_8zBW&K@1O=E#)$zEtAoQlH9x|se6DM)ec9YVr6Q?E7 zwvy6lw*d?P4})3ORv;84|0`wClraQmXSpE(gc5B-SBp zDk-fwHD(NpX+p8s9RXnz#DEx6B8nD`$Kze13KSp%RaVrn5{H@!0T9<8iCd#q0p|39 zbz6LwluC?wP?jr|N~EGi2rbG=B9RCIVSmo&P<7w<*m-!s4G_gEOhS7vS zkk)aUXrJZqz1SXz_VcqImUGxAWoq&w7divCJeZ~!F+%f%OgAz;MX{!5Y*cmR*?rFI0um7*yi_g7VT^h@t-l zTP7?J>L8wfJLp!~bBlFrh5@s#&yZ{4te%v3Xc61CU?$NQ)j?bXAd^;x_4(bGTOzpkc8lc1x>$QM(YhRR>eCxVaYhTos zx-xf04!ZNcZfzX#5eUTQ?D0)=da=t#mh#!0d13H#KG&DBdSAYJb!l-qG%vQ#pL+hy zj*a0xH~&gaY+U{9&OUG37L9&7?>&=R)>f{cI(>BN$jTqR^X)G^XTEi6awy&IPhvEyRpU;H8 zU47$&owa8t7A}0R$ZiC2F-UZ}MHaJeh{ z*2i}pcyiOOcfRa8a=TcU__dQhqYOwV&wp|Ax8FNDYOmi|9o_ek@Zq1IEVi9rx*J?t M+02lBw0~m%KWGNDmjD0& literal 0 HcmV?d00001 diff --git a/Play/Play.csproj b/Play/Play.csproj index 039b638..3dd44ac 100644 --- a/Play/Play.csproj +++ b/Play/Play.csproj @@ -38,6 +38,8 @@ true true true + true + 4.0.30816.0 AnyCPU @@ -184,6 +186,12 @@ + + BackgroundTaskHostView.xaml + + + BackgroundTaskTileView.xaml + Dummy.xaml @@ -211,10 +219,23 @@ MainWindow.xaml Code + + MSBuild:Compile + Designer + true + Designer MSBuild:Compile + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -309,6 +330,9 @@ + + + + \ No newline at end of file diff --git a/Play/Views/BackgroundTaskHostView.xaml b/Play/Views/BackgroundTaskHostView.xaml new file mode 100644 index 0000000..c5b884f --- /dev/null +++ b/Play/Views/BackgroundTaskHostView.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Play/Views/BackgroundTaskHostView.xaml.cs b/Play/Views/BackgroundTaskHostView.xaml.cs new file mode 100644 index 0000000..941ac89 --- /dev/null +++ b/Play/Views/BackgroundTaskHostView.xaml.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Play.ViewModels; +using ReactiveUI.Routing; + +namespace Play +{ + /// + /// Interaction logic for BackgroundTaskView.xaml + /// + public partial class BackgroundTaskHostView : UserControl, IViewForViewModel + { + public BackgroundTaskHostView() + { + this.InitializeComponent(); + } + + public IBackgroundTaskHostViewModel ViewModel { + get { return (IBackgroundTaskHostViewModel)GetValue(ViewModelProperty); } + set { SetValue(ViewModelProperty, value); } + } + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register("ViewModel", typeof(IBackgroundTaskHostViewModel), typeof(BackgroundTaskHostView), new UIPropertyMetadata(null)); + + object IViewForViewModel.ViewModel { get { return ViewModel; } set { ViewModel = (IBackgroundTaskHostViewModel)value; } } + } +} \ No newline at end of file diff --git a/Play/Views/BackgroundTaskTileView.xaml b/Play/Views/BackgroundTaskTileView.xaml new file mode 100644 index 0000000..3582238 --- /dev/null +++ b/Play/Views/BackgroundTaskTileView.xaml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/Play/Views/BackgroundTaskTileView.xaml.cs b/Play/Views/BackgroundTaskTileView.xaml.cs new file mode 100644 index 0000000..4868cf2 --- /dev/null +++ b/Play/Views/BackgroundTaskTileView.xaml.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Play +{ + /// + /// Interaction logic for BackgroundTaskTileView.xaml + /// + public partial class BackgroundTaskTileView : UserControl + { + public BackgroundTaskTileView() + { + this.InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Play/Views/Dummy.xaml b/Play/Views/Dummy.xaml new file mode 100644 index 0000000..9cc7309 --- /dev/null +++ b/Play/Views/Dummy.xaml @@ -0,0 +1,69 @@ + + + + + + + + diff --git a/Play/Views/Dummy.xaml.cs b/Play/Views/Dummy.xaml.cs new file mode 100644 index 0000000..25e89d7 --- /dev/null +++ b/Play/Views/Dummy.xaml.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Play.Views +{ + /// + /// Interaction logic for Dummy.xaml + /// + public partial class Dummy : UserControl + { + public Dummy() + { + InitializeComponent(); + } + } +} From d83341f00cbec86c3bd1221c1ef00320c83f868f Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Fri, 6 Jul 2012 22:19:15 -0700 Subject: [PATCH 10/12] Restructure some ViewModels --- Play/ViewModels/AppBootstrapper.cs | 2 +- .../ViewModels/BackgroundTaskTileViewModel.cs | 26 +++++++++++++++++++ Play/ViewModels/PlayViewModel.cs | 14 ++++------ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Play/ViewModels/AppBootstrapper.cs b/Play/ViewModels/AppBootstrapper.cs index 8f7b0fc..a8f4c6e 100644 --- a/Play/ViewModels/AppBootstrapper.cs +++ b/Play/ViewModels/AppBootstrapper.cs @@ -118,7 +118,7 @@ IKernel createDefaultKernel() ret.Bind().ToConstant(this); ret.Bind().To(); ret.Bind().To(); - ret.Bind().To(); + ret.Bind().To(); ret.Bind().To(); ret.Bind>().To(); ret.Bind>().To(); diff --git a/Play/ViewModels/BackgroundTaskTileViewModel.cs b/Play/ViewModels/BackgroundTaskTileViewModel.cs index f36aea9..18731ab 100644 --- a/Play/ViewModels/BackgroundTaskTileViewModel.cs +++ b/Play/ViewModels/BackgroundTaskTileViewModel.cs @@ -6,11 +6,18 @@ using System.Reactive.Subjects; using System.Text; using System.Threading; +using System.Windows; using ReactiveUI; using ReactiveUI.Xaml; namespace Play.ViewModels { + public interface IBackgroundTaskHostViewModel : IReactiveNotifyPropertyChanged + { + ReactiveCollection BackgroundTasks { get; } + Visibility ShouldShowBackgroundTaskPane { get; } + } + public interface IBackgroundTaskTileViewModel : IReactiveNotifyPropertyChanged { int CurrentProgress { get; } @@ -20,6 +27,25 @@ public interface IBackgroundTaskTileViewModel : IReactiveNotifyPropertyChanged ReactiveCommand Cancel { get; } } + public class BackgroundTaskHostViewModel : ReactiveObject, IBackgroundTaskHostViewModel + { + public ReactiveCollection BackgroundTasks { get; protected set; } + + ObservableAsPropertyHelper _ShouldShowBackgroundTaskPane; + public Visibility ShouldShowBackgroundTaskPane { + get { return _ShouldShowBackgroundTaskPane.Value; } + } + + public BackgroundTaskHostViewModel() + { + BackgroundTasks = new ReactiveCollection(); + + BackgroundTasks.CollectionCountChanged + .Select(x => x == 0 ? Visibility.Visible : Visibility.Hidden) + .ToProperty(this, x => x.ShouldShowBackgroundTaskPane); + } + } + public class BackgroundTaskUserError : UserError { public BackgroundTaskUserError(string errorMessage, Exception innerException) : base(errorMessage, innerException: innerException) diff --git a/Play/ViewModels/PlayViewModel.cs b/Play/ViewModels/PlayViewModel.cs index 6c56cbe..fc9b420 100644 --- a/Play/ViewModels/PlayViewModel.cs +++ b/Play/ViewModels/PlayViewModel.cs @@ -18,12 +18,7 @@ namespace Play.ViewModels { - public interface IBackgroundTaskHostViewModel : IReactiveNotifyPropertyChanged - { - ReactiveCollection BackgroundTasks { get; } - } - - public interface IPlayViewModel : IRoutableViewModel, IBackgroundTaskHostViewModel + public interface IPlayViewModel : IRoutableViewModel { IPlayApi AuthenticatedClient { get; } string ListenUrl { get; } @@ -34,6 +29,7 @@ public interface IPlayViewModel : IRoutableViewModel, IBackgroundTaskHostViewMod ReactiveCommand TogglePlay { get; } ReactiveCommand Search { get; } ReactiveCommand Logout { get; } + IBackgroundTaskHostViewModel BackgroundTaskHost { get; } } public class PlayViewModel : ReactiveObject, IPlayViewModel @@ -75,17 +71,17 @@ public bool IsPlaying { set { this.RaiseAndSetIfChanged(x => x.IsPlaying, value); } } - public ReactiveCollection BackgroundTasks { get; protected set; } - public ReactiveCommand TogglePlay { get; protected set; } public ReactiveCommand Search { get; protected set; } public ReactiveCommand Logout { get; protected set; } + public IBackgroundTaskHostViewModel BackgroundTaskHost { get; protected set; } + [Inject] public PlayViewModel(IScreen screen, ILoginMethods loginMethods) { HostScreen = screen; - BackgroundTasks = new ReactiveCollection(); + BackgroundTaskHost = RxApp.GetService(); TogglePlay = new ReactiveCommand(); Logout = new ReactiveCommand(); Search = new ReactiveCommand(); From fba80752ae885f8fc987ceaf1b5fc68e0dc67e14 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Fri, 6 Jul 2012 22:41:38 -0700 Subject: [PATCH 11/12] Add some tests to make sure album/song downloads queue background UI tasks --- Play.Tests/IntegrationTestUrl.cs | 4 +- Play.Tests/Play.Tests.csproj | 1 + .../ViewModels/BackgroundTaskTileViewModel.cs | 71 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Play.Tests/ViewModels/BackgroundTaskTileViewModel.cs diff --git a/Play.Tests/IntegrationTestUrl.cs b/Play.Tests/IntegrationTestUrl.cs index ef06e56..9add0bc 100644 --- a/Play.Tests/IntegrationTestUrl.cs +++ b/Play.Tests/IntegrationTestUrl.cs @@ -10,14 +10,14 @@ public static class IntegrationTestUrl { public static string Current { get { - //return "https://play.yourcompany.com"; + return "https://play.githubapp.com"; throw new Exception("Configure IntegrationTestUrl.cs first"); } } public static string Token { get { - //return "a04d03"; + return "f07c8b"; throw new Exception("Configure IntegrationTestUrl.cs first"); } } diff --git a/Play.Tests/Play.Tests.csproj b/Play.Tests/Play.Tests.csproj index 1ce6bb4..bbb2f26 100644 --- a/Play.Tests/Play.Tests.csproj +++ b/Play.Tests/Play.Tests.csproj @@ -125,6 +125,7 @@ + diff --git a/Play.Tests/ViewModels/BackgroundTaskTileViewModel.cs b/Play.Tests/ViewModels/BackgroundTaskTileViewModel.cs new file mode 100644 index 0000000..cc3c461 --- /dev/null +++ b/Play.Tests/ViewModels/BackgroundTaskTileViewModel.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Text; +using System.Windows.Media.Imaging; +using Akavache; +using FluentAssertions; +using Moq; +using Ninject; +using Ninject.MockingKernel.Moq; +using Play.Models; +using Play.ViewModels; +using ReactiveUI; +using Xunit; + +namespace Play.Tests.ViewModels +{ + public class BackgroundTaskTileViewModelTests + { + [Fact] + public void DownloadAlbumShouldQueueABackgroundTask() + { + var kernel = new MoqMockingKernel(); + var taskHost = new BackgroundTaskHostViewModel(); + kernel.Bind().ToConstant(taskHost); + + var fixture = setupStandardFixture(Fakes.GetSong(), kernel); + + var list = new List(); + taskHost.BackgroundTasks.CollectionCountChanged.Subscribe(list.Add); + fixture.DownloadAlbum.Execute(null); + + list.Contains(1).Should().BeTrue(); + } + + [Fact] + public void DownloadSongShouldQueueABackgroundTask() + { + var kernel = new MoqMockingKernel(); + var taskHost = new BackgroundTaskHostViewModel(); + kernel.Bind().ToConstant(taskHost); + + var fixture = setupStandardFixture(Fakes.GetSong(), kernel); + + var list = new List(); + taskHost.BackgroundTasks.CollectionCountChanged.Subscribe(list.Add); + fixture.DownloadSong.Execute(null); + + list.Contains(1).Should().BeTrue(); + } + + static ISongTileViewModel setupStandardFixture(Song song, MoqMockingKernel kernel) + { + kernel.Bind().To().Named("UserAccount"); + kernel.Bind().To().Named("LocalMachine"); + RxApp.ConfigureServiceLocator((t,s) => kernel.Get(t,s), (t,s) => kernel.GetAll(t,s)); + + kernel.GetMock().Setup(x => x.FetchImageForAlbum(It.IsAny())) + .Returns(Observable.Return(new BitmapImage())); + + kernel.GetMock().Setup(x => x.DownloadAlbum(It.IsAny(), It.IsAny())) + .Returns(Observable.Return>(null)); + kernel.GetMock().Setup(x => x.DownloadSong(It.IsAny())) + .Returns(Observable.Return>(null)); + + return new SongTileViewModel(song, kernel.Get()); + } + } +} From b61ff82485546ccdd081ccdea62faa95a6b48337 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Sat, 7 Jul 2012 15:06:20 -0700 Subject: [PATCH 12/12] More View hacking --- Play/ViewModels/AppBootstrapper.cs | 3 ++- Play/ViewModels/SongTileViewModel.cs | 4 ++-- Play/Views/BackgroundTaskHostView.xaml | 18 ++++++++++-------- Play/Views/BackgroundTaskHostView.xaml.cs | 8 ++++---- Play/Views/BackgroundTaskTileView.xaml | 6 +++--- Play/Views/BackgroundTaskTileView.xaml.cs | 13 ++++++++++++- Play/Views/PlayView.xaml | 5 ++++- 7 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Play/ViewModels/AppBootstrapper.cs b/Play/ViewModels/AppBootstrapper.cs index a8f4c6e..8cb53bf 100644 --- a/Play/ViewModels/AppBootstrapper.cs +++ b/Play/ViewModels/AppBootstrapper.cs @@ -118,12 +118,13 @@ IKernel createDefaultKernel() ret.Bind().ToConstant(this); ret.Bind().To(); ret.Bind().To(); - ret.Bind().To(); ret.Bind().To(); + ret.Bind().To(); ret.Bind>().To(); ret.Bind>().To(); ret.Bind>().To(); ret.Bind>().To().InTransientScope(); + ret.Bind>().To(); #if DEBUG var testBlobCache = new TestBlobCache(); diff --git a/Play/ViewModels/SongTileViewModel.cs b/Play/ViewModels/SongTileViewModel.cs index c1b741e..95819e3 100644 --- a/Play/ViewModels/SongTileViewModel.cs +++ b/Play/ViewModels/SongTileViewModel.cs @@ -177,14 +177,14 @@ IObservable postProcessAlbum(Tuple fileAndData, string roo if (!targetDir.Exists) targetDir.Create(); // NB: https://github.com/play/play/issues/169 + /* using (var archive = TarArchive.CreateInputTarArchive(new MemoryStream(fileAndData.Item2))) { archive.ExtractContents(targetDir.FullName); } + */ - /* var zip = new FastZip(); zip.ExtractZip(new MemoryStream(fileAndData.Item2), targetDir.FullName, FastZip.Overwrite.Always, null, null, null, true, true); - */ }, RxApp.TaskpoolScheduler); } } diff --git a/Play/Views/BackgroundTaskHostView.xaml b/Play/Views/BackgroundTaskHostView.xaml index c5b884f..af01433 100644 --- a/Play/Views/BackgroundTaskHostView.xaml +++ b/Play/Views/BackgroundTaskHostView.xaml @@ -5,10 +5,10 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Routing="clr-namespace:ReactiveUI.Routing;assembly=ReactiveUI.Routing" mc:Ignorable="d" x:Class="Play.BackgroundTaskHostView" - x:Name="UserControl" + x:Name="theHostView" d:DesignWidth="640" d:DesignHeight="480"> - + @@ -17,12 +17,14 @@ - - - - - - + + + + + + + + diff --git a/Play/Views/BackgroundTaskHostView.xaml.cs b/Play/Views/BackgroundTaskHostView.xaml.cs index 941ac89..e92dfb5 100644 --- a/Play/Views/BackgroundTaskHostView.xaml.cs +++ b/Play/Views/BackgroundTaskHostView.xaml.cs @@ -18,20 +18,20 @@ namespace Play /// /// Interaction logic for BackgroundTaskView.xaml /// - public partial class BackgroundTaskHostView : UserControl, IViewForViewModel + public partial class BackgroundTaskHostView : UserControl, IViewForViewModel { public BackgroundTaskHostView() { this.InitializeComponent(); } - public IBackgroundTaskHostViewModel ViewModel { - get { return (IBackgroundTaskHostViewModel)GetValue(ViewModelProperty); } + public BackgroundTaskHostViewModel ViewModel { + get { return (BackgroundTaskHostViewModel)GetValue(ViewModelProperty); } set { SetValue(ViewModelProperty, value); } } public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(IBackgroundTaskHostViewModel), typeof(BackgroundTaskHostView), new UIPropertyMetadata(null)); - object IViewForViewModel.ViewModel { get { return ViewModel; } set { ViewModel = (IBackgroundTaskHostViewModel)value; } } + object IViewForViewModel.ViewModel { get { return ViewModel; } set { ViewModel = (BackgroundTaskHostViewModel)value; } } } } \ No newline at end of file diff --git a/Play/Views/BackgroundTaskTileView.xaml b/Play/Views/BackgroundTaskTileView.xaml index 3582238..fe7b68c 100644 --- a/Play/Views/BackgroundTaskTileView.xaml +++ b/Play/Views/BackgroundTaskTileView.xaml @@ -5,10 +5,10 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="Play.BackgroundTaskTileView" - x:Name="UserControl" - d:DesignWidth="480" d:DesignHeight="128"> + x:Name="UserControl" Height="64" + d:DesignWidth="480" d:DesignHeight="64"> - + diff --git a/Play/Views/BackgroundTaskTileView.xaml.cs b/Play/Views/BackgroundTaskTileView.xaml.cs index 4868cf2..4d7fc92 100644 --- a/Play/Views/BackgroundTaskTileView.xaml.cs +++ b/Play/Views/BackgroundTaskTileView.xaml.cs @@ -10,17 +10,28 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Play.ViewModels; +using ReactiveUI.Routing; namespace Play { /// /// Interaction logic for BackgroundTaskTileView.xaml /// - public partial class BackgroundTaskTileView : UserControl + public partial class BackgroundTaskTileView : UserControl, IViewForViewModel { public BackgroundTaskTileView() { this.InitializeComponent(); } + + public BackgroundTaskTileViewModel ViewModel { + get { return (BackgroundTaskTileViewModel)GetValue(ViewModelProperty); } + set { SetValue(ViewModelProperty, value); } + } + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register("ViewModel", typeof(BackgroundTaskTileViewModel), typeof(BackgroundTaskTileView), new UIPropertyMetadata(null)); + + object IViewForViewModel.ViewModel { get { return ViewModel; } set { ViewModel = (BackgroundTaskTileViewModel)value; } } } } \ No newline at end of file diff --git a/Play/Views/PlayView.xaml b/Play/Views/PlayView.xaml index 4d1bc16..433e9da 100644 --- a/Play/Views/PlayView.xaml +++ b/Play/Views/PlayView.xaml @@ -8,6 +8,7 @@ + @@ -88,8 +89,10 @@ + + - +