mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-16 10:03:51 -07:00
signalr cleanup
This commit is contained in:
parent
feda4a9b67
commit
25e2c98c45
219 changed files with 2035 additions and 1495 deletions
|
@ -1,9 +1,9 @@
|
|||
{{#if folder.path}}
|
||||
<div class="row well unmapped-folder-path">
|
||||
<div class="span11">
|
||||
{{folder.path}}
|
||||
</div>
|
||||
<div class="row well unmapped-folder-path">
|
||||
<div class="span11">
|
||||
{{folder.path}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="row x-search-bar">
|
||||
<div class="input-prepend nz-input-large add-series-search span11">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
|
@ -8,10 +8,19 @@ define(
|
|||
|
||||
var CommandCollection = Backbone.Collection.extend({
|
||||
url : window.ApiRoot + '/command',
|
||||
model: CommandModel
|
||||
model: CommandModel,
|
||||
|
||||
findCommand: function (command) {
|
||||
return this.find(function (model) {
|
||||
return model.isSameCommand(command);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var collection = new CommandCollection().bindSignalR();
|
||||
|
||||
collection.fetch();
|
||||
|
||||
return collection;
|
||||
});
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
'use strict';
|
||||
define({
|
||||
Execute: function (name, properties) {
|
||||
var data = { command: name };
|
||||
define(
|
||||
[
|
||||
'Commands/CommandModel',
|
||||
'Commands/CommandCollection',
|
||||
'underscore'
|
||||
], function (CommandModel, CommandCollection, _) {
|
||||
|
||||
if (properties) {
|
||||
$.extend(data, properties);
|
||||
return{
|
||||
Execute: function (name, properties) {
|
||||
|
||||
var attr = _.extend({name: name.toLocaleLowerCase()}, properties);
|
||||
|
||||
var commandModel = new CommandModel(attr);
|
||||
|
||||
return commandModel.save().success(function () {
|
||||
CommandCollection.add(commandModel);
|
||||
});
|
||||
}
|
||||
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url : window.ApiRoot + '/command',
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
15
UI/Commands/CommandMessengerCollectionView.js
Normal file
15
UI/Commands/CommandMessengerCollectionView.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Commands/CommandCollection',
|
||||
'Commands/CommandMessengerItemView'
|
||||
], function (App, Marionette, commandCollection, CommandMessengerItemView) {
|
||||
|
||||
var CollectionView = Marionette.CollectionView.extend({
|
||||
itemView : CommandMessengerItemView
|
||||
});
|
||||
|
||||
new CollectionView({collection: commandCollection});
|
||||
});
|
48
UI/Commands/CommandMessengerItemView.js
Normal file
48
UI/Commands/CommandMessengerItemView.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Shared/Messenger'
|
||||
], function (App, Marionette, Messenger) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
},
|
||||
|
||||
|
||||
render: function () {
|
||||
if (!this.model.get('message')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message = {
|
||||
type : 'info',
|
||||
message : '[{0}] {1}'.format(this.model.get('name'), this.model.get('message')),
|
||||
id : this.model.id,
|
||||
hideAfter: 0
|
||||
};
|
||||
|
||||
switch (this.model.get('state')) {
|
||||
case 'completed':
|
||||
message.hideAfter = 4;
|
||||
break;
|
||||
case 'failed':
|
||||
message.hideAfter = 4;
|
||||
message.type = 'error';
|
||||
break;
|
||||
default :
|
||||
message.hideAfter = 0;
|
||||
}
|
||||
|
||||
Messenger.show(message);
|
||||
|
||||
console.log(message.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -4,5 +4,30 @@ define(
|
|||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
url: window.ApiRoot + '/command',
|
||||
|
||||
parse: function (response) {
|
||||
response.name = response.name.toLocaleLowerCase();
|
||||
return response;
|
||||
},
|
||||
|
||||
isActive: function () {
|
||||
return this.get('state') !== 'completed' && this.get('state') !== 'failed';
|
||||
},
|
||||
|
||||
isSameCommand: function (command) {
|
||||
|
||||
if (command.name.toLocaleLowerCase() != this.get('name').toLocaleLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var key in command) {
|
||||
if (key !== 'name' && command[key] !== this.get(key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,6 @@ define(
|
|||
'Series/Details/SeriesDetailsLayout',
|
||||
'Series/SeriesCollection',
|
||||
'Missing/MissingLayout',
|
||||
'Series/SeriesModel',
|
||||
'Calendar/CalendarLayout',
|
||||
'Logs/Layout',
|
||||
'Logs/Files/Layout',
|
||||
|
@ -19,7 +18,7 @@ define(
|
|||
'SeasonPass/Layout',
|
||||
'Shared/NotFoundView',
|
||||
'Shared/Modal/Region'
|
||||
], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, SeriesCollection, MissingLayout, SeriesModel, CalendarLayout,
|
||||
], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, SeriesCollection, MissingLayout, CalendarLayout,
|
||||
LogsLayout, LogFileLayout, ReleaseLayout, SystemLayout, SeasonPassLayout, NotFoundView) {
|
||||
return Marionette.Controller.extend({
|
||||
|
||||
|
|
|
@ -7,11 +7,9 @@ define(
|
|||
'Episode/Search/ManualLayout',
|
||||
'Release/Collection',
|
||||
'Series/SeriesCollection',
|
||||
'Shared/LoadingView',
|
||||
'Shared/Messenger',
|
||||
'Shared/Actioneer',
|
||||
'Shared/FormatHelpers'
|
||||
], function (App, Marionette, ButtonsView, ManualSearchLayout, ReleaseCollection, SeriesCollection, LoadingView, Messenger, Actioneer, FormatHelpers) {
|
||||
'Commands/CommandController',
|
||||
'Shared/LoadingView'
|
||||
], function (App, Marionette, ButtonsView, ManualSearchLayout, ReleaseCollection, SeriesCollection,CommandController, LoadingView) {
|
||||
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Episode/Search/LayoutTemplate',
|
||||
|
@ -39,18 +37,8 @@ define(
|
|||
e.preventDefault();
|
||||
}
|
||||
|
||||
var series = SeriesCollection.get(this.model.get('seriesId'));
|
||||
var seriesTitle = series.get('title');
|
||||
var season = this.model.get('seasonNumber');
|
||||
var episode = this.model.get('episodeNumber');
|
||||
var message = seriesTitle + ' - ' + season + 'x' + FormatHelpers.pad(episode, 2);
|
||||
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'episodeSearch',
|
||||
properties: {
|
||||
episodeId: this.model.get('id')
|
||||
},
|
||||
errorMessage: 'Search failed for: ' + message
|
||||
CommandController.Execute('episodeSearch', {
|
||||
episodeId: this.model.get('id')
|
||||
});
|
||||
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
|
|
|
@ -5,59 +5,29 @@ define(
|
|||
], function () {
|
||||
|
||||
_.extend(Backbone.Collection.prototype, {
|
||||
bindSignalR: function (options) {
|
||||
bindSignalR: function () {
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
var collection = this;
|
||||
|
||||
if (!options.url) {
|
||||
console.assert(this.url, 'url must be provided or collection must have url');
|
||||
options.url = this.url.replace('api', 'signalr');
|
||||
}
|
||||
var processMessage = function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var _getStatus = function (status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'connecting';
|
||||
case 1:
|
||||
return 'connected';
|
||||
case 2:
|
||||
return 'reconnecting';
|
||||
case 4:
|
||||
return 'disconnected';
|
||||
default:
|
||||
throw 'invalid status ' + status;
|
||||
}
|
||||
var model = new collection.model(options.resource, {parse: true});
|
||||
collection.add(model, {merge: true});
|
||||
console.log(options.action + ": %O", options.resource);
|
||||
};
|
||||
|
||||
this.signalRconnection = $.connection(options.url);
|
||||
|
||||
this.signalRconnection.stateChanged(function (change) {
|
||||
console.debug('{0} [{1}]'.format(options.url, _getStatus(change.newState)));
|
||||
});
|
||||
|
||||
this.signalRconnection.received(function (message) {
|
||||
console.debug(message);
|
||||
self.fetch();
|
||||
});
|
||||
|
||||
this.signalRconnection.start({ transport:
|
||||
require(
|
||||
[
|
||||
'longPolling'
|
||||
] });
|
||||
'app'
|
||||
], function (app) {
|
||||
collection.listenTo(app.vent, 'server:' + collection.url.replace('/api/', ''), processMessage)
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
unbindSignalR: function () {
|
||||
|
||||
if(this.signalRconnection){
|
||||
this.signalRconnection.stop();
|
||||
delete this.signalRconnection;
|
||||
}
|
||||
|
||||
}});
|
||||
});
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'backbone',
|
||||
'ProgressMessaging/ProgressMessageModel',
|
||||
'Shared/Messenger',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (Backbone, ProgressMessageModel, Messenger) {
|
||||
], function (App, Backbone, Messenger) {
|
||||
|
||||
var ProgressMessageCollection = Backbone.Collection.extend({
|
||||
url : window.ApiRoot + '/progressmessage',
|
||||
model: ProgressMessageModel
|
||||
model: Backbone.Model,
|
||||
|
||||
initialize: function(){
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var collection = new ProgressMessageCollection().bindSignalR();
|
||||
var collection = new ProgressMessageCollection();//.bindSignalR();
|
||||
|
||||
collection.signalRconnection.received(function (message) {
|
||||
/* collection.signalRconnection.received(function (message) {
|
||||
|
||||
var type = getMessengerType(message.status);
|
||||
var hideAfter = type === 'info' ? 60 : 5;
|
||||
var type = getMessengerType(message.status);
|
||||
var hideAfter = type === 'info' ? 60 :5;
|
||||
|
||||
Messenger.show({
|
||||
id : message.commandId,
|
||||
message : message.message,
|
||||
type : type,
|
||||
hideAfter: hideAfter
|
||||
});
|
||||
});
|
||||
Messenger.show({
|
||||
id : message.commandId,
|
||||
message : message.message,
|
||||
type : type,
|
||||
hideAfter: hideAfter
|
||||
});
|
||||
});*/
|
||||
|
||||
var getMessengerType = function (status) {
|
||||
switch (status) {
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
});
|
||||
});
|
13
UI/Router.js
13
UI/Router.js
|
@ -6,11 +6,12 @@ require(
|
|||
'Controller',
|
||||
'Series/SeriesCollection',
|
||||
'ProgressMessaging/ProgressMessageCollection',
|
||||
'Commands/CommandMessengerCollectionView',
|
||||
'Shared/Actioneer',
|
||||
'Navbar/NavbarView',
|
||||
'jQuery/RouteBinder',
|
||||
'jquery'
|
||||
], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, Actioneer, NavbarView, RouterBinder, $) {
|
||||
], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, CommandMessengerCollectionView, Actioneer, NavbarView, RouterBinder, $) {
|
||||
|
||||
var Router = Marionette.AppRouter.extend({
|
||||
|
||||
|
@ -40,11 +41,11 @@ require(
|
|||
App.Router = new Router();
|
||||
|
||||
SeriesCollection.fetch().done(function () {
|
||||
Backbone.history.start({ pushState: true });
|
||||
RouterBinder.bind(App.Router);
|
||||
App.navbarRegion.show(new NavbarView());
|
||||
$('body').addClass('started');
|
||||
});
|
||||
Backbone.history.start({ pushState: true });
|
||||
RouterBinder.bind(App.Router);
|
||||
App.navbarRegion.show(new NavbarView());
|
||||
$('body').addClass('started');
|
||||
});
|
||||
});
|
||||
|
||||
return App.Router;
|
||||
|
|
|
@ -3,8 +3,8 @@ define(
|
|||
[
|
||||
'marionette',
|
||||
'Series/SeasonCollection',
|
||||
'Shared/Actioneer'
|
||||
], function (Marionette, SeasonCollection, Actioneer) {
|
||||
'Cells/ToggleCell'
|
||||
], function (Marionette, Backgrid, SeasonCollection, ToggleCell) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'SeasonPass/SeriesLayoutTemplate',
|
||||
|
||||
|
|
|
@ -1,35 +1,41 @@
|
|||
<div class="series-season" id="season-{{seasonNumber}}">
|
||||
<h2>
|
||||
<i class="x-season-monitored clickable" title="Toggle season monitored status"/>
|
||||
|
||||
|
||||
{{#if seasonNumber}}
|
||||
Season {{seasonNumber}}
|
||||
Season {{seasonNumber}}
|
||||
{{else}}
|
||||
Specials
|
||||
Specials
|
||||
{{/if}}
|
||||
|
||||
{{#if_eq episodeCount compare=0}}
|
||||
<i class="icon-nd-status season-status status-primary" title="No aired episodes"/>
|
||||
<i class="icon-nd-status season-status status-primary" title="No aired episodes"/>
|
||||
{{else}}
|
||||
{{#if_eq percentOfEpisodes compare=100}}
|
||||
<i class="icon-nd-status season-status status-success" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{else}}
|
||||
<i class="icon-nd-status season-status status-danger" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{/if_eq}}
|
||||
{{#if_eq percentOfEpisodes compare=100}}
|
||||
<i class="icon-nd-status season-status status-success" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{else}}
|
||||
<i class="icon-nd-status season-status status-danger" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{/if_eq}}
|
||||
{{/if_eq}}
|
||||
|
||||
<span class="season-actions pull-right">
|
||||
<i class="icon-nd-rename x-season-rename" title="Rename all episodes in season {{seasonNumber}}"/>
|
||||
<i class="icon-search x-season-search" title="Search for all episodes in season {{seasonNumber}}"/>
|
||||
<div class="x-season-rename">
|
||||
<i class="icon-nd-rename" title="Rename all episodes in season {{seasonNumber}}"/>
|
||||
</div>
|
||||
<div class="x-season-search">
|
||||
<i class="icon-search" title="Search for all episodes in season {{seasonNumber}}"/>
|
||||
</div>
|
||||
</span>
|
||||
</h2>
|
||||
<div class="x-episode-grid"></div>
|
||||
<div class="show-hide-episodes x-show-hide-episodes">
|
||||
<h4>
|
||||
{{#if showingEpisodes}}
|
||||
<i class="icon-chevron-sign-up"/> Hide Episodes
|
||||
<i class="icon-chevron-sign-up"/>
|
||||
Hide Episodes
|
||||
{{else}}
|
||||
<i class="icon-chevron-sign-down"/> Show Episodes
|
||||
<i class="icon-chevron-sign-down"/>
|
||||
Show Episodes
|
||||
{{/if}}
|
||||
</h4>
|
||||
</div>
|
||||
|
|
|
@ -8,27 +8,20 @@ define(
|
|||
'Series/SeasonCollection',
|
||||
'Series/Details/SeasonCollectionView',
|
||||
'Series/Details/InfoView',
|
||||
'Commands/CommandController',
|
||||
'Shared/LoadingView',
|
||||
'Shared/Actioneer',
|
||||
'backstrech',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (App,
|
||||
Marionette,
|
||||
EpisodeCollection,
|
||||
EpisodeFileCollection,
|
||||
SeasonCollection,
|
||||
SeasonCollectionView,
|
||||
InfoView,
|
||||
LoadingView,
|
||||
Actioneer) {
|
||||
], function (App, Marionette, EpisodeCollection, EpisodeFileCollection, SeasonCollection, SeasonCollectionView, InfoView, CommandController, LoadingView, Actioneer) {
|
||||
return Marionette.Layout.extend({
|
||||
|
||||
itemViewContainer: '.x-series-seasons',
|
||||
template : 'Series/Details/SeriesDetailsTemplate',
|
||||
|
||||
regions: {
|
||||
seasons : '#seasons',
|
||||
info : '#info'
|
||||
seasons: '#seasons',
|
||||
info : '#info'
|
||||
},
|
||||
|
||||
ui: {
|
||||
|
@ -73,6 +66,32 @@ define(
|
|||
this._showSeasons();
|
||||
this._setMonitoredState();
|
||||
this._showInfo();
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
onRender: function(){
|
||||
Actioneer.bindToCommand({
|
||||
element: this.ui.refresh,
|
||||
command: {
|
||||
name : 'refreshSeries'
|
||||
}
|
||||
});
|
||||
|
||||
Actioneer.bindToCommand({
|
||||
element: this.ui.search,
|
||||
command: {
|
||||
name : 'seriesSearch'
|
||||
}
|
||||
});
|
||||
|
||||
Actioneer.bindToCommand({
|
||||
element: this.ui.rename,
|
||||
command: {
|
||||
name : 'renameSeries'
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getFanArt: function () {
|
||||
|
@ -127,15 +146,9 @@ define(
|
|||
},
|
||||
|
||||
_refreshSeries: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'refreshSeries',
|
||||
properties: {
|
||||
seriesId: this.model.get('id')
|
||||
},
|
||||
element : this.ui.refresh,
|
||||
leaveIcon : true,
|
||||
context : this,
|
||||
onSuccess : this._showSeasons
|
||||
CommandController.Execute('refreshSeries', {
|
||||
name : 'refreshSeries',
|
||||
seriesId: this.model.id
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -147,27 +160,18 @@ define(
|
|||
},
|
||||
|
||||
_renameSeries: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'renameSeries',
|
||||
properties : {
|
||||
seriesId: this.model.get('id')
|
||||
},
|
||||
element : this.ui.rename,
|
||||
context : this,
|
||||
onSuccess : this._refetchEpisodeFiles,
|
||||
errorMessage: 'Series search failed'
|
||||
|
||||
CommandController.Execute('renameSeries', {
|
||||
name : 'renameSeries',
|
||||
seriesId: this.model.id
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_seriesSearch: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'seriesSearch',
|
||||
properties : {
|
||||
seriesId: this.model.get('id')
|
||||
},
|
||||
element : this.ui.search,
|
||||
errorMessage: 'Series search failed',
|
||||
startMessage: 'Search for {0} started'.format(this.model.get('title'))
|
||||
CommandController.Execute('seriesSearch', {
|
||||
name : 'seriesSearch',
|
||||
seriesId: this.model.id
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -187,15 +191,10 @@ define(
|
|||
series : self.model
|
||||
});
|
||||
|
||||
App.reqres.setHandler(App.Reqres.GetEpisodeFileById, function(episodeFileId){
|
||||
App.reqres.setHandler(App.Reqres.GetEpisodeFileById, function (episodeFileId) {
|
||||
return self.episodeFileCollection.get(episodeFileId);
|
||||
});
|
||||
|
||||
/* self.episodeCollection.bindSignalR({
|
||||
onReceived: seasonCollectionView.onEpisodeGrabbed,
|
||||
context : seasonCollectionView
|
||||
});*/
|
||||
|
||||
self.seasons.show(seasonCollectionView);
|
||||
});
|
||||
},
|
||||
|
@ -208,7 +207,7 @@ define(
|
|||
this.episodeFileCollection.fetch();
|
||||
},
|
||||
|
||||
_onSeasonRenamed: function(event) {
|
||||
_onSeasonRenamed: function (event) {
|
||||
if (this.model.get('id') === event.series.get('id')) {
|
||||
this._refetchEpisodeFiles();
|
||||
}
|
||||
|
|
|
@ -4,12 +4,21 @@
|
|||
<h1>
|
||||
<i class="icon-bookmark x-monitored clickable" title="Toggle monitored state for entire series"/>
|
||||
{{title}}
|
||||
<span class="series-actions pull-right">
|
||||
<i class="icon-refresh x-refresh" title="Update series info and scan disk"/>
|
||||
<i class="icon-nd-rename x-rename" title="Rename all episodes in this series"/>
|
||||
<i class="icon-search x-search" title="Search for all episodes in this series"/>
|
||||
<i class="icon-nd-edit x-edit" title="Edit series"/>
|
||||
</span>
|
||||
<div class="series-actions pull-right">
|
||||
<div class="x-refresh">
|
||||
<i class="icon-refresh icon-can-spin" title="Update series info and scan disk"/>
|
||||
</div>
|
||||
<div class="x-rename">
|
||||
<i class="icon-nd-rename" title="Rename all episodes in this series"/>
|
||||
</div>
|
||||
<div class="x-search">
|
||||
<i class="icon-search" title="Search for all episodes in this series"/>
|
||||
</div>
|
||||
<div>
|
||||
<i class="icon-nd-edit" title="Edit series"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row series-detail-overview">
|
||||
|
|
|
@ -6,18 +6,18 @@
|
|||
overflow : visible;
|
||||
|
||||
.series-poster {
|
||||
padding-left: 20px;
|
||||
width : 168px;
|
||||
padding-left : 20px;
|
||||
width : 168px;
|
||||
}
|
||||
|
||||
.form-horizontal {
|
||||
margin-top: 10px;
|
||||
margin-top : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-series-modal {
|
||||
.path {
|
||||
margin-left: 30px;
|
||||
margin-left : 30px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
|||
|
||||
.show-hide-episodes {
|
||||
.clickable();
|
||||
text-align: center;
|
||||
text-align : center;
|
||||
|
||||
i {
|
||||
.clickable();
|
||||
|
@ -84,24 +84,24 @@
|
|||
text-align : center;
|
||||
|
||||
.progress {
|
||||
text-align: left;
|
||||
text-align : left;
|
||||
margin-top : 5px;
|
||||
left: 0px;
|
||||
width: 170px;
|
||||
left : 0px;
|
||||
width : 170px;
|
||||
|
||||
.progressbar-front-text {
|
||||
width: 170px;
|
||||
width : 170px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-container {
|
||||
position: relative;
|
||||
position : relative;
|
||||
|
||||
.title {
|
||||
position : absolute;
|
||||
top : -100px;
|
||||
opacity: 0.0;
|
||||
top : -100px;
|
||||
opacity : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@
|
|||
.episode-detail-modal {
|
||||
|
||||
.episode-info {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
|
||||
.episode-overview {
|
||||
|
@ -221,35 +221,41 @@
|
|||
}
|
||||
|
||||
.season-actions, .series-actions {
|
||||
font-size : 24px;
|
||||
|
||||
div {
|
||||
display : inline-block
|
||||
}
|
||||
|
||||
text-transform : none;
|
||||
|
||||
i {
|
||||
.clickable;
|
||||
font-size : 24px;
|
||||
padding-left : 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.series-stats {
|
||||
font-size: 11px;
|
||||
font-size : 11px;
|
||||
}
|
||||
|
||||
.series-legend {
|
||||
padding-top: 5px;
|
||||
padding-top : 5px;
|
||||
}
|
||||
|
||||
.seasonpass-series {
|
||||
.card;
|
||||
margin : 20px 0px;
|
||||
margin : 20px 0px;
|
||||
|
||||
.title {
|
||||
font-weight: 300;
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
margin-left: 5px;
|
||||
font-weight : 300;
|
||||
font-size : 24px;
|
||||
line-height : 30px;
|
||||
margin-left : 5px;
|
||||
}
|
||||
|
||||
.season-select {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom : 0px;
|
||||
}
|
||||
|
||||
.expander {
|
||||
|
@ -260,11 +266,11 @@
|
|||
}
|
||||
|
||||
.season-grid {
|
||||
margin-top: 10px;
|
||||
margin-top : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.season-status {
|
||||
font-size: 16px;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
font-size : 16px;
|
||||
vertical-align : middle !important;
|
||||
}
|
||||
|
|
|
@ -3,17 +3,55 @@ define(
|
|||
[
|
||||
'Commands/CommandController',
|
||||
'Commands/CommandCollection',
|
||||
'Shared/Messenger'],
|
||||
function(CommandController, CommandCollection, Messenger) {
|
||||
'Shared/Messenger',
|
||||
'jQuery/jquery.spin'
|
||||
], function (CommandController, CommandCollection, Messenger) {
|
||||
|
||||
var actioneer = Marionette.AppRouter.extend({
|
||||
|
||||
initialize: function () {
|
||||
this.trackedCommands = [];
|
||||
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);
|
||||
|
||||
|
@ -62,7 +100,7 @@ define(
|
|||
_handleCommands: function () {
|
||||
var self = this;
|
||||
|
||||
_.each(this.trackedCommands, function (trackedCommand){
|
||||
_.each(this.trackedCommands, function (trackedCommand) {
|
||||
if (trackedCommand.completed === true) {
|
||||
return;
|
||||
}
|
||||
|
@ -95,12 +133,12 @@ define(
|
|||
});
|
||||
},
|
||||
|
||||
_getIconClass: function(element) {
|
||||
_getIconClass: function (element) {
|
||||
if (!element) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return element.attr('class').match(/(?:^|\s)icon\-.+?(?:$|\s)/)[0];
|
||||
return element.find('i').attr('class').match(/(?:^|\s)icon\-.+?(?:$|\s)/)[0];
|
||||
},
|
||||
|
||||
_setSpinnerOnElement: function (options) {
|
||||
|
@ -195,4 +233,4 @@ define(
|
|||
});
|
||||
|
||||
return new actioneer();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette'
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
'use strict';
|
||||
define(function () {
|
||||
return {
|
||||
|
||||
show: function (options) {
|
||||
|
||||
if (!options.type) {
|
||||
options.type = 'info';
|
||||
}
|
||||
|
||||
if (!options.hideAfter) {
|
||||
if (options.hideAfter === undefined) {
|
||||
switch (options.type) {
|
||||
case 'info':
|
||||
options.hideAfter = 5;
|
||||
|
@ -18,7 +19,7 @@ define(function () {
|
|||
break;
|
||||
|
||||
default :
|
||||
options.hideAfter = 0;
|
||||
options.hideAfter = 5;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +32,7 @@ define(function () {
|
|||
});
|
||||
},
|
||||
|
||||
|
||||
monitor: function (options) {
|
||||
|
||||
if (!options.promise) {
|
||||
|
|
51
UI/Shared/SignalRBroadcaster.js
Normal file
51
UI/Shared/SignalRBroadcaster.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'signalR'
|
||||
], function () {
|
||||
return {
|
||||
|
||||
appInitializer: function () {
|
||||
console.log('starting signalR');
|
||||
|
||||
var getStatus = function (status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'connecting';
|
||||
case 1:
|
||||
return 'connected';
|
||||
case 2:
|
||||
return 'reconnecting';
|
||||
case 4:
|
||||
return 'disconnected';
|
||||
default:
|
||||
throw 'invalid status ' + status;
|
||||
}
|
||||
};
|
||||
|
||||
this.signalRconnection = $.connection("/signalr");
|
||||
|
||||
this.signalRconnection.stateChanged(function (change) {
|
||||
console.debug('SignalR: [{0}]'.format(getStatus(change.newState)));
|
||||
});
|
||||
|
||||
this.signalRconnection.received(function (message) {
|
||||
require(
|
||||
[
|
||||
'app'
|
||||
], function (app) {
|
||||
app.vent.trigger('server:' + message.name, message.body);
|
||||
})
|
||||
});
|
||||
|
||||
this.signalRconnection.start({ transport:
|
||||
[
|
||||
'longPolling'
|
||||
] });
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -3,9 +3,9 @@ define(
|
|||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Shared/Actioneer',
|
||||
'Shared/Messenger'
|
||||
], function (App, Marionette, Actioneer, Messenger) {
|
||||
'Commands/CommandController',
|
||||
'Shared/Actioneer'
|
||||
], function (App, Marionette, CommandController, Actioneer) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template : 'Shared/Toolbar/ButtonTemplate',
|
||||
|
@ -15,13 +15,8 @@ define(
|
|||
'click': 'onClick'
|
||||
},
|
||||
|
||||
ui: {
|
||||
icon: '.x-icon'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.storageKey = this.model.get('menuKey') + ':' + this.model.get('key');
|
||||
this.idle = true;
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
|
@ -30,33 +25,34 @@ define(
|
|||
this.invokeCallback();
|
||||
}
|
||||
|
||||
if(!this.model.get('title')){
|
||||
if (!this.model.get('title')) {
|
||||
this.$el.addClass('btn-icon-only');
|
||||
}
|
||||
|
||||
var command = this.model.get('command');
|
||||
if (command) {
|
||||
Actioneer.bindToCommand({
|
||||
command: {name: command},
|
||||
element: this.$el
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function () {
|
||||
if (this.idle) {
|
||||
this.invokeCallback();
|
||||
this.invokeRoute();
|
||||
this.invokeCommand();
|
||||
|
||||
if (this.$el.hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.invokeCallback();
|
||||
this.invokeRoute();
|
||||
this.invokeCommand();
|
||||
},
|
||||
|
||||
invokeCommand: function () {
|
||||
var command = this.model.get('command');
|
||||
if (command) {
|
||||
this.idle = false;
|
||||
|
||||
Actioneer.ExecuteCommand({
|
||||
command : command,
|
||||
button : this.$el,
|
||||
element : this.ui.icon,
|
||||
errorMessage : this.model.get('errorMessage'),
|
||||
successMessage: this.model.get('successMessage'),
|
||||
always : this._commandAlways,
|
||||
context : this
|
||||
});
|
||||
CommandController.Execute(command);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -83,12 +79,6 @@ define(
|
|||
if (callback) {
|
||||
callback.call(this.model.ownerContext);
|
||||
}
|
||||
},
|
||||
|
||||
_commandAlways: function () {
|
||||
if (!this.isClosed) {
|
||||
this.idle = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
|
@ -6,15 +6,13 @@ define(
|
|||
'System/StatusModel',
|
||||
'System/About/View',
|
||||
'Logs/Layout',
|
||||
'Shared/Toolbar/ToolbarLayout',
|
||||
'Shared/LoadingView'
|
||||
'Shared/Toolbar/ToolbarLayout'
|
||||
], function (App,
|
||||
Marionette,
|
||||
StatusModel,
|
||||
AboutView,
|
||||
LogsLayout,
|
||||
ToolbarLayout,
|
||||
LoadingView) {
|
||||
ToolbarLayout) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'System/LayoutTemplate',
|
||||
|
||||
|
@ -38,21 +36,7 @@ define(
|
|||
title : 'Check for Update',
|
||||
icon : 'icon-nd-update',
|
||||
command: 'applicationUpdate'
|
||||
},
|
||||
// {
|
||||
// title : 'Restart',
|
||||
// icon : 'icon-repeat',
|
||||
// command : 'restart',
|
||||
// successMessage: 'NzbDrone restart has been triggered',
|
||||
// errorMessage : 'Failed to restart NzbDrone'
|
||||
// },
|
||||
// {
|
||||
// title : 'Shutdown',
|
||||
// icon : 'icon-power-off',
|
||||
// command : 'shutdown',
|
||||
// successMessage: 'NzbDrone shutdown has started',
|
||||
// errorMessage : 'Failed to shutdown NzbDrone'
|
||||
// }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
|
|
@ -181,8 +181,9 @@ require.config({
|
|||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Shared/SignalRBroadcaster',
|
||||
'Instrumentation/StringFormat'
|
||||
], function (Marionette) {
|
||||
], function (Marionette, SignalRBroadcaster) {
|
||||
|
||||
var app = new Marionette.Application();
|
||||
|
||||
|
@ -210,6 +211,8 @@ define(
|
|||
console.log('starting application');
|
||||
});
|
||||
|
||||
app.addInitializer(SignalRBroadcaster.appInitializer, {app: app});
|
||||
|
||||
app.addRegions({
|
||||
navbarRegion: '#nav-region',
|
||||
mainRegion : '#main-region',
|
||||
|
|
45
UI/jQuery/jquery.spin.js
Normal file
45
UI/jQuery/jquery.spin.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
define(
|
||||
[
|
||||
'jquery'
|
||||
], function ($) {
|
||||
'use strict';
|
||||
|
||||
$.fn.startSpin = function () {
|
||||
|
||||
var icon = this.find('i');
|
||||
|
||||
var iconClasses = icon.attr('class').match(/(?:^|\s)icon\-.+?(?:$|\s)/);
|
||||
|
||||
if (iconClasses.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var iconClass = $.trim(iconClasses[0]);
|
||||
|
||||
this.addClass('disabled');
|
||||
|
||||
if (icon.hasClass('icon-can-spin')) {
|
||||
icon.addClass('icon-spin');
|
||||
}
|
||||
else {
|
||||
icon.attr('data-idle-icon', iconClass);
|
||||
icon.removeClass(iconClass);
|
||||
icon.addClass('icon-nd-spinner');
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
$.fn.stopSpin = function () {
|
||||
var icon = this.find('i');
|
||||
|
||||
this.removeClass('disabled');
|
||||
icon.removeClass('icon-spin icon-nd-spinner');
|
||||
var idleIcon = icon.attr('data-idle-icon');
|
||||
|
||||
if (idleIcon) {
|
||||
icon.addClass(idleIcon);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue