diff --git a/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs b/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs index f197bd287..5e6221578 100644 --- a/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs +++ b/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs @@ -5,6 +5,14 @@ namespace NzbDrone.Core.Notifications.Email { public class TestEmailCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string Server { get; set; } public int Port { get; set; } public bool Ssl { get; set; } diff --git a/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs b/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs index fac4c70a2..77f251bed 100644 --- a/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs +++ b/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs @@ -7,6 +7,13 @@ namespace NzbDrone.Core.Notifications.Growl { public class TestGrowlCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string Host { get; set; } public int Port { get; set; } public string Password { get; set; } diff --git a/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs b/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs index 9c4c6b4aa..9676342b7 100644 --- a/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs +++ b/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs @@ -7,6 +7,13 @@ namespace NzbDrone.Core.Notifications.Plex { public class TestPlexClientCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string Host { get; set; } public int Port { get; set; } public string Username { get; set; } diff --git a/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs b/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs index c32f5c1b0..38fb3e9e5 100644 --- a/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs +++ b/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs @@ -7,6 +7,14 @@ namespace NzbDrone.Core.Notifications.Plex { public class TestPlexServerCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string Host { get; set; } public int Port { get; set; } } diff --git a/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs b/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs index 51b5e2a84..c54da875f 100644 --- a/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs +++ b/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs @@ -7,6 +7,13 @@ namespace NzbDrone.Core.Notifications.Prowl { public class TestProwlCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string ApiKey { get; set; } public int Priority { get; set; } } diff --git a/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs b/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs index 01a909cda..83ec23054 100644 --- a/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs +++ b/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs @@ -7,6 +7,14 @@ namespace NzbDrone.Core.Notifications.Pushover { public class TestPushoverCommand : Command { + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string UserKey { get; set; } public int Priority { get; set; } } diff --git a/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs b/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs index 983cb2ba6..3d90e9697 100644 --- a/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs +++ b/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs @@ -7,6 +7,14 @@ namespace NzbDrone.Core.Notifications.Xbmc { public class TestXbmcCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string Host { get; set; } public int Port { get; set; } public string Username { get; set; } diff --git a/UI/Commands/CommandController.js b/UI/Commands/CommandController.js index 0749b7970..650d8f67b 100644 --- a/UI/Commands/CommandController.js +++ b/UI/Commands/CommandController.js @@ -3,10 +3,12 @@ define( [ 'Commands/CommandModel', 'Commands/CommandCollection', - 'underscore' + 'underscore', + 'jQuery/jquery.spin' ], function (CommandModel, CommandCollection, _) { return{ + Execute: function (name, properties) { var attr = _.extend({name: name.toLocaleLowerCase()}, properties); @@ -16,6 +18,39 @@ define( return commandModel.save().success(function () { CommandCollection.add(commandModel); }); + }, + + bindToCommand: function (options) { + + var self = this; + + var existingCommand = CommandCollection.findCommand(options.command); + + if (existingCommand) { + this._bindToCommandModel.call(this, existingCommand, options); + } + + CommandCollection.bind('add sync', function (model) { + if (model.isSameCommand(options.command)) { + self._bindToCommandModel.call(self, model, options); + } + }); + }, + + _bindToCommandModel: function bindToCommand(model, options) { + + if (!model.isActive()) { + options.element.stopSpin(); + return; + } + + model.bind('change:state', function (model) { + if (!model.isActive()) { + options.element.stopSpin(); + } + }); + + options.element.startSpin(); } } }); diff --git a/UI/Router.js b/UI/Router.js index 8fd614e49..de2111644 100644 --- a/UI/Router.js +++ b/UI/Router.js @@ -7,11 +7,10 @@ require( 'Series/SeriesCollection', 'ProgressMessaging/ProgressMessageCollection', 'Commands/CommandMessengerCollectionView', - 'Shared/Actioneer', 'Navbar/NavbarView', 'jQuery/RouteBinder', 'jquery' - ], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, CommandMessengerCollectionView, Actioneer, NavbarView, RouterBinder, $) { + ], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, CommandMessengerCollectionView, NavbarView, RouterBinder, $) { var Router = Marionette.AppRouter.extend({ diff --git a/UI/SeasonPass/SeriesLayout.js b/UI/SeasonPass/SeriesLayout.js index fe5c07f23..1eae0ce0e 100644 --- a/UI/SeasonPass/SeriesLayout.js +++ b/UI/SeasonPass/SeriesLayout.js @@ -2,9 +2,9 @@ define( [ 'marionette', - 'Series/SeasonCollection', - 'Cells/ToggleCell' - ], function (Marionette, Backgrid, SeasonCollection, ToggleCell) { + 'backgrid', + 'Series/SeasonCollection' + ], function (Marionette, Backgrid, SeasonCollection) { return Marionette.Layout.extend({ template: 'SeasonPass/SeriesLayoutTemplate', @@ -111,11 +111,9 @@ define( this.model.setSeasonMonitored(seasonNumber); - Actioneer.SaveModel({ - element: element, - context: this, - always : this._afterToggleSeasonMonitored - }); + var savePromise =this.model.save() + .always(this.render.bind(this)); + element.spinForPromise(savePromise); }, _afterToggleSeasonMonitored: function () { diff --git a/UI/Series/Details/SeasonLayout.js b/UI/Series/Details/SeasonLayout.js index d6234ee87..72cb21225 100644 --- a/UI/Series/Details/SeasonLayout.js +++ b/UI/Series/Details/SeasonLayout.js @@ -8,9 +8,9 @@ define( 'Cells/EpisodeTitleCell', 'Cells/RelativeDateCell', 'Cells/EpisodeStatusCell', - 'Shared/Actioneer', + 'Commands/CommandController', 'moment' - ], function (App, Marionette, Backgrid, ToggleCell, EpisodeTitleCell, RelativeDateCell, EpisodeStatusCell, Actioneer, Moment) { + ], function (App, Marionette, Backgrid, ToggleCell, EpisodeTitleCell, RelativeDateCell, EpisodeStatusCell, CommandController, Moment) { return Marionette.Layout.extend({ template: 'Series/Details/SeasonLayoutTemplate', @@ -21,11 +21,11 @@ define( }, events: { - 'click .x-season-search' : '_seasonSearch', - 'click .x-season-monitored' : '_seasonMonitored', - 'click .x-season-rename' : '_seasonRename', - 'click .x-show-hide-episodes' : '_showHideEpisodes', - 'dblclick .series-season h2' : '_showHideEpisodes' + 'click .x-season-monitored' : '_seasonMonitored', + 'click .x-season-search' : '_seasonSearch', + 'click .x-season-rename' : '_seasonRename', + 'click .x-show-hide-episodes': '_showHideEpisodes', + 'dblclick .series-season h2' : '_showHideEpisodes' }, regions: { @@ -51,11 +51,11 @@ define( }) }, { - name : 'this', - label : 'Title', - hideSeriesLink : true, - cell : EpisodeTitleCell, - sortable: false + name : 'this', + label : 'Title', + hideSeriesLink: true, + cell : EpisodeTitleCell, + sortable : false }, { name : 'airDateUtc', @@ -75,42 +75,60 @@ define( if (!options.episodeCollection) { throw 'episodeCollection is needed'; } - - this.templateHelpers = {}; + this.templateHelpers = this.templateHelpers || {}; this.episodeCollection = options.episodeCollection.bySeason(this.model.get('seasonNumber')); this.series = options.series; this.showingEpisodes = this._shouldShowEpisodes(); - this._generateTemplateHelpers(); - this.listenTo(this.model, 'sync', function () { - this._afterSeasonMonitored(); - }, this); - - this.listenTo(this.episodeCollection, 'sync', function () { - this.render(); - }, this); + this.listenTo(this.model, 'sync', this._afterSeasonMonitored); + this.listenTo(this.episodeCollection, 'sync', this.render); }, onRender: function () { + + this._generateTemplateHelpers(); + if (this.showingEpisodes) { this._showEpisodes(); } this._setSeasonMonitoredState(); + + CommandController.bindToCommand({ + element: this.ui.seasonSearch, + command: { + name: 'seasonSearch', + seriesId : this.series.id, + seasonNumber: this.model.get('seasonNumber') + } + }); + + CommandController.bindToCommand({ + element: this.ui.seasonRename, + command: { + name: 'renameSeason', + seriesId : this.series.id, + seasonNumber: this.model.get('seasonNumber') + } + }); }, _seasonSearch: function () { - Actioneer.ExecuteCommand({ - command : 'seasonSearch', - properties : { - seriesId : this.model.get('seriesId'), - seasonNumber: this.model.get('seasonNumber') - }, - element : this.ui.seasonSearch, - errorMessage : 'Search for season {0} failed'.format(this.model.get('seasonNumber')), - startMessage : 'Search for season {0} started'.format(this.model.get('seasonNumber')), - successMessage: 'Search for season {0} completed'.format(this.model.get('seasonNumber')) + + CommandController.Execute('seasonSearch', { + name: 'seasonSearch', + seriesId : this.series.id, + seasonNumber: this.model.get('seasonNumber') + }); + }, + + _seasonRename: function () { + + CommandController.Execute('renameSeason', { + name: 'renameSeason', + seriesId : this.series.id, + seasonNumber: this.model.get('seasonNumber') }); }, @@ -119,12 +137,10 @@ define( this.model.set(name, !this.model.get(name)); this.series.setSeasonMonitored(this.model.get('seasonNumber')); - Actioneer.SaveModel({ - model : this.series, - context: this, - element: this.ui.seasonMonitored, - always : this._afterSeasonMonitored - }); + var savePromise = this.series.save() + .always(this._afterSeasonMonitored.bind(this)); + + this.ui.seasonMonitored.spinForPromise(savePromise); }, _afterSeasonMonitored: function () { @@ -150,19 +166,7 @@ define( } }, - _seasonRename: function () { - Actioneer.ExecuteCommand({ - command : 'renameSeason', - properties : { - seriesId : this.model.get('seriesId'), - seasonNumber: this.model.get('seasonNumber') - }, - element : this.ui.seasonRename, - errorMessage: 'Season rename failed', - context : this, - onSuccess : this._afterRename - }); - }, + _afterRename: function () { App.vent.trigger(App.Events.SeasonRenamed, { series: this.series, seasonNumber: this.model.get('seasonNumber') }); @@ -180,12 +184,11 @@ define( var startDate = Moment().add('month', -1); var endDate = Moment().add('year', 1); - var result = this.episodeCollection.some(function(episode) { + var result = this.episodeCollection.some(function (episode) { var airDate = episode.get('airDateUtc'); - if (airDate) - { + if (airDate) { var airDateMoment = Moment(airDate); if (airDateMoment.isAfter(startDate) && airDateMoment.isBefore(endDate)) { @@ -203,7 +206,7 @@ define( this.templateHelpers.showingEpisodes = this.showingEpisodes; var episodeCount = this.episodeCollection.filter(function (episode) { - return (episode.get('monitored') && Moment(episode.get('airDateUtc')).isBefore(Moment())) || episode.get('hasFile'); + return (episode.get('monitored') && Moment(episode.get('airDateUtc')).isBefore(Moment())) || episode.get('hasFile'); }).length; var episodeFileCount = this.episodeCollection.where({ hasFile: true }).length; @@ -221,14 +224,11 @@ define( _showHideEpisodes: function () { if (this.showingEpisodes) { this.showingEpisodes = false; - this.episodeGrid.$el.slideUp(); this.episodeGrid.close(); } - else { this.showingEpisodes = true; this._showEpisodes(); - this.episodeGrid.$el.slideDown(); } this.templateHelpers.showingEpisodes = this.showingEpisodes; diff --git a/UI/Series/Details/SeriesDetailsLayout.js b/UI/Series/Details/SeriesDetailsLayout.js index 4067584d9..ec6fc38c9 100644 --- a/UI/Series/Details/SeriesDetailsLayout.js +++ b/UI/Series/Details/SeriesDetailsLayout.js @@ -10,10 +10,9 @@ define( 'Series/Details/InfoView', 'Commands/CommandController', 'Shared/LoadingView', - 'Shared/Actioneer', 'backstrech', 'Mixins/backbone.signalr.mixin' - ], function (App, Marionette, EpisodeCollection, EpisodeFileCollection, SeasonCollection, SeasonCollectionView, InfoView, CommandController, LoadingView, Actioneer) { + ], function (App, Marionette, EpisodeCollection, EpisodeFileCollection, SeasonCollection, SeasonCollectionView, InfoView, CommandController, LoadingView) { return Marionette.Layout.extend({ itemViewContainer: '.x-series-seasons', @@ -67,21 +66,21 @@ define( }, onRender: function () { - Actioneer.bindToCommand({ + CommandController.bindToCommand({ element: this.ui.refresh, command: { name: 'refreshSeries' } }); - Actioneer.bindToCommand({ + CommandController.bindToCommand({ element: this.ui.search, command: { name: 'seriesSearch' } }); - Actioneer.bindToCommand({ + CommandController.bindToCommand({ element: this.ui.rename, command: { name: 'renameSeries' @@ -195,13 +194,9 @@ define( this.info.show(new InfoView({ model: this.model })); }, - _refetchEpisodeFiles: function () { - this.episodeFileCollection.fetch(); - }, - _onSeasonRenamed: function (event) { if (this.model.get('id') === event.series.get('id')) { - this._refetchEpisodeFiles(); + this.episodeFileCollection.fetch(); } } }); diff --git a/UI/Settings/Notifications/EditView.js b/UI/Settings/Notifications/EditView.js index be47a7db6..244ea8e18 100644 --- a/UI/Settings/Notifications/EditView.js +++ b/UI/Settings/Notifications/EditView.js @@ -6,11 +6,11 @@ define([ 'Settings/Notifications/Model', 'Settings/Notifications/DeleteView', 'Shared/Messenger', - 'Shared/Actioneer', + 'Commands/CommandController', 'Mixins/AsModelBoundView', 'Form/FormBuilder' -], function (App, Marionette, NotificationModel, DeleteView, Messenger, Actioneer, AsModelBoundView) { +], function (App, Marionette, NotificationModel, DeleteView, Messenger, CommandController, AsModelBoundView) { var model = Marionette.ItemView.extend({ template: 'Settings/Notifications/EditTemplate', @@ -76,16 +76,8 @@ define([ properties[field.name] = field.value; }); - Actioneer.ExecuteCommand({ - command : testCommand, - properties : properties, - button : this.ui.testButton, - element : this.ui.testIcon, - errorMessage : 'Failed to test notification settings', - successMessage: 'Notification settings tested successfully', - always : this._testOnAlways, - context : this - }); + + CommandController.Execute(testCommand, properties); } }, diff --git a/UI/Shared/Actioneer.js b/UI/Shared/Actioneer.js deleted file mode 100644 index 42d11cdb2..000000000 --- a/UI/Shared/Actioneer.js +++ /dev/null @@ -1,236 +0,0 @@ -'use strict'; -define( - [ - 'Commands/CommandController', - 'Commands/CommandCollection', - 'Shared/Messenger', - 'jQuery/jquery.spin' - ], function (CommandController, CommandCollection, Messenger) { - - var actioneer = Marionette.AppRouter.extend({ - - initialize: function () { - this.trackedCommands = - [ - ]; - CommandCollection.fetch(); - this.listenTo(CommandCollection, 'sync', this._handleCommands); - }, - - - bindToCommand: function (options) { - - var self = this; - options.idleIcon = this._getIconClass(options.element); - - var existingCommand = CommandCollection.findCommand(options.command); - - if (existingCommand) { - this._bindToCommandModel.call(this, existingCommand, options); - } - - this.listenTo(CommandCollection, 'add sync', function (model) { - if (model.isSameCommand(options.command)) { - self._bindToCommandModel.call(self, model, options); - } - }); - }, - - _bindToCommandModel: function bindToCommand(model, options) { - - if (!model.isActive()) { - options.element.stopSpin(); - return; - } - - this.listenTo(model, 'change:state', function (model) { - if (!model.isActive()) { - options.element.stopSpin(); - } - }); - - options.element.startSpin(); - }, - - ExecuteCommand: function (options) { - options.iconClass = this._getIconClass(options.element); - - if (options.button) { - options.button.addClass('disable'); - } - - this._setSpinnerOnElement(options); - - var promise = CommandController.Execute(options.command, options.properties); - this._showStartMessage(options, promise); - }, - - SaveModel: function (options) { - options.iconClass = this._getIconClass(options.element); - - this._showStartMessage(options); - this._setSpinnerOnElement(options); - - var model = options.model ? options.model : options.context.model; - - var promise = model.save(); - this._handlePromise(promise, options); - }, - - _handlePromise: function (promise, options) { - var self = this; - - promise.done(function () { - self._onSuccess(options); - }); - - promise.fail(function (ajaxOptions) { - if (ajaxOptions.readyState === 0 || ajaxOptions.status === 0) { - return; - } - - self._onError(options); - }); - - promise.always(function () { - self._onComplete(options); - }); - }, - - _handleCommands: function () { - var self = this; - - _.each(this.trackedCommands, function (trackedCommand) { - if (trackedCommand.completed === true) { - return; - } - - var options = trackedCommand.options; - var command = CommandCollection.find({ 'id': trackedCommand.id }); - - if (!command) { - trackedCommand.completed = true; - - self._onError(options, trackedCommand.id); - self._onComplete(options); - return; - } - - if (command.get('state') === 'completed') { - trackedCommand.completed = true; - - self._onSuccess(options, command.get('id')); - self._onComplete(options); - return; - } - - if (command.get('state') === 'failed') { - trackedCommand.completed = true; - - self._onError(options, command.get('id')); - self._onComplete(options); - } - }); - }, - - _getIconClass: function (element) { - if (!element) { - return ''; - } - - return element.find('i').attr('class').match(/(?:^|\s)icon\-.+?(?:$|\s)/)[0]; - }, - - _setSpinnerOnElement: function (options) { - if (!options.element) { - return; - } - - if (options.leaveIcon) { - options.element.addClass('icon-spin'); - } - - else { - options.element.removeClass(options.iconClass); - options.element.addClass('icon-nd-spinner'); - } - }, - - _onSuccess: function (options, id) { - if (options.successMessage) { - Messenger.show({ - id : id, - message: options.successMessage, - type : 'success' - }); - } - - if (options.onSuccess) { - options.onSuccess.call(options.context); - } - }, - - _onError: function (options, id) { - if (options.errorMessage) { - Messenger.show({ - id : id, - message: options.errorMessage, - type : 'error' - }); - } - - if (options.onError) { - options.onError.call(options.context); - } - }, - - _onComplete: function (options) { - if (options.button) { - options.button.removeClass('disable'); - } - - if (options.element) { - if (options.leaveIcon) { - options.element.removeClass('icon-spin'); - } - - else { - options.element.addClass(options.iconClass); - options.element.removeClass('icon-nd-spinner'); - options.element.removeClass('icon-spin'); - } - } - - if (options.always) { - options.always.call(options.context); - } - }, - - _showStartMessage: function (options, promise) { - var self = this; - - if (!promise) { - if (options.startMessage) { - Messenger.show({ - message: options.startMessage - }); - } - - return; - } - - promise.done(function (data) { - self.trackedCommands.push({ id: data.id, options: options }); - - if (options.startMessage) { - Messenger.show({ - id : data.id, - message: options.startMessage - }); - } - }); - } - }); - - return new actioneer(); - }); diff --git a/UI/Shared/Toolbar/Button/ButtonView.js b/UI/Shared/Toolbar/Button/ButtonView.js index 54949cc9c..1bcd36679 100644 --- a/UI/Shared/Toolbar/Button/ButtonView.js +++ b/UI/Shared/Toolbar/Button/ButtonView.js @@ -3,9 +3,8 @@ define( [ 'app', 'marionette', - 'Commands/CommandController', - 'Shared/Actioneer' - ], function (App, Marionette, CommandController, Actioneer) { + 'Commands/CommandController' + ], function (App, Marionette, CommandController) { return Marionette.ItemView.extend({ template : 'Shared/Toolbar/ButtonTemplate', @@ -31,7 +30,7 @@ define( var command = this.model.get('command'); if (command) { - Actioneer.bindToCommand({ + CommandController.bindToCommand({ command: {name: command}, element: this.$el });