diff --git a/Gruntfile.js b/Gruntfile.js index d78fac4da..7025f8c6b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -73,6 +73,7 @@ module.exports = function (grunt) { 'UI/Cells/cells.less', 'UI/Logs/logs.less', 'UI/Settings/settings.less', + 'UI/Update/update.less' ], dest : outputRoot, ext: '.css' diff --git a/NzbDrone.Api/Update/UpdateModule.cs b/NzbDrone.Api/Update/UpdateModule.cs index fbe13bd8e..e4a8cbf3a 100644 --- a/NzbDrone.Api/Update/UpdateModule.cs +++ b/NzbDrone.Api/Update/UpdateModule.cs @@ -10,25 +10,33 @@ namespace NzbDrone.Api.Update public class UpdateModule : NzbDroneRestModule { private readonly ICheckUpdateService _checkUpdateService; + private readonly IRecentUpdateProvider _recentUpdateProvider; - public UpdateModule(ICheckUpdateService checkUpdateService) + public UpdateModule(ICheckUpdateService checkUpdateService, + IRecentUpdateProvider recentUpdateProvider) { _checkUpdateService = checkUpdateService; - GetResourceAll = GetAvailableUpdate; + _recentUpdateProvider = recentUpdateProvider; + GetResourceAll = GetRecentUpdates; } - private List GetAvailableUpdate() + private UpdateResource GetAvailableUpdate() { var update = _checkUpdateService.AvailableUpdate(); - var response = new List(); + var response = new UpdateResource(); if (update != null) { - response.Add(update.InjectTo()); + return update.InjectTo(); } return response; } + + private List GetRecentUpdates() + { + return ToListResource(_recentUpdateProvider.GetRecentUpdatePackages); + } } public class UpdateResource : RestResource @@ -40,5 +48,7 @@ namespace NzbDrone.Api.Update public DateTime ReleaseDate { get; set; } public String FileName { get; set; } public String Url { get; set; } + + public UpdateChanges Changes { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 99987c84b..58b09e81e 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -252,7 +252,7 @@ Always - + Always diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 34bc316bb..23d3657e3 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -550,6 +550,8 @@ + + diff --git a/NzbDrone.Core/Update/RecentUpdateProvider.cs b/NzbDrone.Core/Update/RecentUpdateProvider.cs new file mode 100644 index 000000000..feee0d34f --- /dev/null +++ b/NzbDrone.Core/Update/RecentUpdateProvider.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.Update +{ + public interface IRecentUpdateProvider + { + List GetRecentUpdatePackages(); + } + + public class RecentUpdateProvider : IRecentUpdateProvider + { + private readonly IConfigFileProvider _configFileProvider; + private readonly IUpdatePackageProvider _updatePackageProvider; + + public RecentUpdateProvider(IConfigFileProvider configFileProvider, + IUpdatePackageProvider updatePackageProvider) + { + _configFileProvider = configFileProvider; + _updatePackageProvider = updatePackageProvider; + } + + public List GetRecentUpdatePackages() + { + var branch = _configFileProvider.Branch; + return _updatePackageProvider.GetRecentUpdates(branch); + } + } +} diff --git a/NzbDrone.Core/Update/UpdateChanges.cs b/NzbDrone.Core/Update/UpdateChanges.cs new file mode 100644 index 000000000..a26bba93b --- /dev/null +++ b/NzbDrone.Core/Update/UpdateChanges.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Update +{ + public class UpdateChanges + { + public List New { get; set; } + public List Fixed { get; set; } + + public UpdateChanges() + { + New = new List(); + Fixed = new List(); + } + } +} diff --git a/NzbDrone.Core/Update/UpdatePackage.cs b/NzbDrone.Core/Update/UpdatePackage.cs index f94a77e4b..f8159686a 100644 --- a/NzbDrone.Core/Update/UpdatePackage.cs +++ b/NzbDrone.Core/Update/UpdatePackage.cs @@ -13,5 +13,7 @@ namespace NzbDrone.Core.Update public DateTime ReleaseDate { get; set; } public String FileName { get; set; } public String Url { get; set; } + + public UpdateChanges Changes { get; set; } } } diff --git a/NzbDrone.Core/Update/UpdatePackageProvider.cs b/NzbDrone.Core/Update/UpdatePackageProvider.cs index c6b4b64c0..29a9073df 100644 --- a/NzbDrone.Core/Update/UpdatePackageProvider.cs +++ b/NzbDrone.Core/Update/UpdatePackageProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using NzbDrone.Common; using RestSharp; using NzbDrone.Core.Rest; @@ -8,6 +9,7 @@ namespace NzbDrone.Core.Update public interface IUpdatePackageProvider { UpdatePackage GetLatestUpdate(string branch, Version currentVersion); + List GetRecentUpdates(string branch); } public class UpdatePackageProvider : IUpdatePackageProvider @@ -27,5 +29,19 @@ namespace NzbDrone.Core.Update return update.UpdatePackage; } + + public List GetRecentUpdates(string branch) + { + var restClient = new RestClient(Services.RootUrl); + + var request = new RestRequest("/v1/update/{branch}/all"); + + request.AddUrlSegment("branch", branch); + request.AddParameter("limit", 5); + + var updates = restClient.ExecuteAndValidate>(request); + + return updates; + } } } \ No newline at end of file diff --git a/UI/.idea/runConfigurations/Debug___Chrome.xml b/UI/.idea/runConfigurations/Debug___Chrome.xml index 2323f096d..82eb4863d 100644 --- a/UI/.idea/runConfigurations/Debug___Chrome.xml +++ b/UI/.idea/runConfigurations/Debug___Chrome.xml @@ -1,25 +1,21 @@ - - - + + + + + + + + + + + + + + + + + diff --git a/UI/.idea/runConfigurations/Debug___Firefox.xml b/UI/.idea/runConfigurations/Debug___Firefox.xml index 1f0cbd78b..2e020afbc 100644 --- a/UI/.idea/runConfigurations/Debug___Firefox.xml +++ b/UI/.idea/runConfigurations/Debug___Firefox.xml @@ -1,25 +1,21 @@ - - - + + + + + + + + + + + + + + + + + diff --git a/UI/Controller.js b/UI/Controller.js index 8f0c059c8..a4bc4d1f9 100644 --- a/UI/Controller.js +++ b/UI/Controller.js @@ -16,10 +16,11 @@ define( 'Release/Layout', 'System/Layout', 'SeasonPass/SeasonPassLayout', + 'Update/UpdateLayout', 'Shared/NotFoundView', 'Shared/Modal/Region' ], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, SeriesCollection, MissingLayout, CalendarLayout, - LogsLayout, LogFileLayout, ReleaseLayout, SystemLayout, SeasonPassLayout, NotFoundView) { + LogsLayout, LogFileLayout, ReleaseLayout, SystemLayout, SeasonPassLayout, UpdateLayout, NotFoundView) { return Marionette.Controller.extend({ series: function () { @@ -94,6 +95,11 @@ define( App.mainRegion.show(new SeasonPassLayout()); }, + update: function () { + this._setTitle('Updates'); + App.mainRegion.show(new UpdateLayout()); + }, + notFound: function () { this._setTitle('Not Found'); App.mainRegion.show(new NotFoundView(this)); diff --git a/UI/Router.js b/UI/Router.js index de2111644..22c13b04f 100644 --- a/UI/Router.js +++ b/UI/Router.js @@ -31,6 +31,7 @@ require( 'rss' : 'rss', 'system' : 'system', 'seasonpass' : 'seasonPass', + 'update' : 'update', ':whatever' : 'notFound' } }); diff --git a/UI/System/Layout.js b/UI/System/Layout.js index 390ce48d8..5f4778c59 100644 --- a/UI/System/Layout.js +++ b/UI/System/Layout.js @@ -33,9 +33,9 @@ define( route: 'logs' }, { - title : 'Check for Update', - icon : 'icon-nd-update', - command: 'applicationUpdate' + title : 'Updates', + icon : 'icon-upload-alt', + route : 'update' } ] }, diff --git a/UI/Update/UpdateCollection.js b/UI/Update/UpdateCollection.js new file mode 100644 index 000000000..b1e7bcf60 --- /dev/null +++ b/UI/Update/UpdateCollection.js @@ -0,0 +1,11 @@ +'use strict'; +define( + [ + 'backbone', + 'Update/UpdateModel' + ], function (Backbone, UpdateModel) { + return Backbone.Collection.extend({ + url : window.NzbDrone.ApiRoot + '/update', + model: UpdateModel + }); + }); diff --git a/UI/Update/UpdateCollectionView.js b/UI/Update/UpdateCollectionView.js new file mode 100644 index 000000000..267af7f85 --- /dev/null +++ b/UI/Update/UpdateCollectionView.js @@ -0,0 +1,10 @@ +'use strict'; +define( + [ + 'marionette', + 'Update/UpdateItemView' + ], function (Marionette, UpdateItemView) { + return Marionette.CollectionView.extend({ + itemView: UpdateItemView + }); + }); diff --git a/UI/Update/UpdateItemView.js b/UI/Update/UpdateItemView.js new file mode 100644 index 000000000..8d478f038 --- /dev/null +++ b/UI/Update/UpdateItemView.js @@ -0,0 +1,11 @@ +'use strict'; + +define( + [ + 'app', + 'marionette' + ], function (App, Marionette) { + return Marionette.ItemView.extend({ + template: 'Update/UpdateItemViewTemplate' + }); + }); diff --git a/UI/Update/UpdateItemViewTemplate.html b/UI/Update/UpdateItemViewTemplate.html new file mode 100644 index 000000000..f932fa618 --- /dev/null +++ b/UI/Update/UpdateItemViewTemplate.html @@ -0,0 +1,23 @@ +
+
+ {{version}} - {{ShortDate releaseDate}} + + {{#with changes}} + {{#each new}} +
+ New {{this}} +
+ {{/each}} + + {{#each fixed}} +
+ Fixed {{this}} +
+ {{/each}} + {{/with}} + + {{#unless changes}} + No notable changes + {{/unless}} +
+
diff --git a/UI/Update/UpdateLayout.js b/UI/Update/UpdateLayout.js new file mode 100644 index 000000000..4794f761c --- /dev/null +++ b/UI/Update/UpdateLayout.js @@ -0,0 +1,58 @@ +'use strict'; +define( + [ + 'marionette', + 'backgrid', + 'Update/UpdateCollection', + 'Update/UpdateCollectionView', + 'Shared/Toolbar/ToolbarLayout', + 'Shared/LoadingView' + ], function (Marionette, Backgrid, UpdateCollection, UpdateCollectionView, ToolbarLayout, LoadingView) { + return Marionette.Layout.extend({ + template: 'Update/UpdateLayoutTemplate', + + regions: { + updates: '#x-updates', + toolbar: '#x-toolbar' + }, + + leftSideButtons: { + type : 'default', + storeState: false, + items : + [ + { + title : 'Check for Update', + icon : 'icon-nd-update', + command: 'applicationUpdate' + } + ] + }, + + initialize: function () { + this.updateCollection = new UpdateCollection(); + }, + + onRender: function () { + this.updates.show(new LoadingView()); + this._showToolbar(); + + var self = this; + var promise = this.updateCollection.fetch(); + + promise.done(function (){ + self.updates.show(new UpdateCollectionView({ collection: self.updateCollection })); + }); + }, + + _showToolbar: function () { + this.toolbar.show(new ToolbarLayout({ + left : + [ + this.leftSideButtons + ], + context: this + })); + } + }); + }); diff --git a/UI/Update/UpdateLayoutTemplate.html b/UI/Update/UpdateLayoutTemplate.html new file mode 100644 index 000000000..c405cb562 --- /dev/null +++ b/UI/Update/UpdateLayoutTemplate.html @@ -0,0 +1,6 @@ +
+
+
+
+
+
diff --git a/UI/Update/UpdateModel.js b/UI/Update/UpdateModel.js new file mode 100644 index 000000000..530a080c6 --- /dev/null +++ b/UI/Update/UpdateModel.js @@ -0,0 +1,9 @@ +'use strict'; +define( + [ + 'backbone' + ], function (Backbone) { + return Backbone.Model.extend({ + + }); + }); diff --git a/UI/Update/update.less b/UI/Update/update.less new file mode 100644 index 000000000..e25b23b70 --- /dev/null +++ b/UI/Update/update.less @@ -0,0 +1,25 @@ +.update { + margin-bottom: 30px; + + legend { + margin-bottom: 5px; + line-height: 30px; + + .date { + font-size: 16px; + } + } + + .changes-header { + font-size: 18px; + } + + .label { + width: 40px; + text-align: center; + } + .change { + margin-bottom: 2px; + font-size: 13px; + } +} \ No newline at end of file diff --git a/UI/index.html b/UI/index.html index c2b0897e7..093473a8a 100644 --- a/UI/index.html +++ b/UI/index.html @@ -15,6 +15,7 @@ +