Skip to content

Commit bf2eae0

Browse files
author
Joseph Atkins-Turkish
committed
Improved error handling and added save button
1 parent 280479a commit bf2eae0

File tree

6 files changed

+61
-27
lines changed

6 files changed

+61
-27
lines changed

ide/migrations/0051_auto__add_publishedmedia__add_unique_publishedmedia_project_name.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,14 @@ def forwards(self, orm):
2525
# Adding unique constraint on 'PublishedMedia', fields ['project', 'name']
2626
db.create_unique(u'ide_publishedmedia', ['project_id', 'name'])
2727

28+
# Adding unique constraint on 'PublishedMedia', fields ['project', 'media_id']
29+
db.create_unique(u'ide_publishedmedia', ['project_id', 'media_id'])
30+
2831

2932
def backwards(self, orm):
33+
# Removing unique constraint on 'PublishedMedia', fields ['project', 'media_id']
34+
db.delete_unique(u'ide_publishedmedia', ['project_id', 'media_id'])
35+
3036
# Removing unique constraint on 'PublishedMedia', fields ['project', 'name']
3137
db.delete_unique(u'ide_publishedmedia', ['project_id', 'name'])
3238

@@ -78,7 +84,7 @@ def backwards(self, orm):
7884
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'builds'", 'to': "orm['ide.Project']"}),
7985
'started': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
8086
'state': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
81-
'uuid': ('django.db.models.fields.CharField', [], {'default': "'8b836ddd-8bc8-4884-bdda-824481f7f05c'", 'max_length': '36'})
87+
'uuid': ('django.db.models.fields.CharField', [], {'default': "'f59cc0ab-c224-4308-9401-e2c34ae88798'", 'max_length': '36'})
8288
},
8389
'ide.buildsize': {
8490
'Meta': {'object_name': 'BuildSize'},
@@ -111,7 +117,7 @@ def backwards(self, orm):
111117
'app_modern_multi_js': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
112118
'app_platforms': ('django.db.models.fields.TextField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
113119
'app_short_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
114-
'app_uuid': ('django.db.models.fields.CharField', [], {'default': "'488507d8-f7d8-449e-b2d9-36c05ba88d4c'", 'max_length': '36', 'null': 'True', 'blank': 'True'}),
120+
'app_uuid': ('django.db.models.fields.CharField', [], {'default': "'2a0eca7f-7b2b-49dd-a5f8-ae6a482b8c4a'", 'max_length': '36', 'null': 'True', 'blank': 'True'}),
115121
'app_version_label': ('django.db.models.fields.CharField', [], {'default': "'1.0'", 'max_length': '40', 'null': 'True', 'blank': 'True'}),
116122
'github_branch': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
117123
'github_hook_build': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
@@ -129,7 +135,7 @@ def backwards(self, orm):
129135
'sdk_version': ('django.db.models.fields.CharField', [], {'default': "'2'", 'max_length': '6'})
130136
},
131137
'ide.publishedmedia': {
132-
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'PublishedMedia'},
138+
'Meta': {'unique_together': "(('project', 'name'), ('project', 'media_id'))", 'object_name': 'PublishedMedia'},
133139
'glance': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
134140
'has_timeline': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
135141
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
@@ -199,7 +205,7 @@ def backwards(self, orm):
199205
'theme': ('django.db.models.fields.CharField', [], {'default': "'cloudpebble'", 'max_length': '50'}),
200206
'use_spaces': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
201207
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
202-
'whats_new': ('django.db.models.fields.PositiveIntegerField', [], {'default': '23'})
208+
'whats_new': ('django.db.models.fields.PositiveIntegerField', [], {'default': '24'})
203209
}
204210
}
205211

ide/models/published_media.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ def clean(self):
5050
raise ValidationError(_("If glance and timeline.tiny are both used, they must be identical."))
5151
if self.has_timeline and not (self.timeline_tiny and self.timeline_small and self.timeline_large):
5252
raise ValidationError(_("If timeline icons are enabled, they must all be set."))
53+
if not self.glance and not self.has_timeline:
54+
raise ValidationError(_("Glance and Timeline cannot both be unset."))
5355
if self.media_id < 0:
5456
raise ValidationError(_("Published Media IDs cannot be negative."))
5557

5658
class Meta(IdeModel.Meta):
57-
unique_together = (('project', 'name'),)
59+
unique_together = (('project', 'name'), ('project', 'media_id'))

ide/static/ide/css/ide.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,9 @@ table.kv-table .kv-remove {
530530
.compilation-pane #run-on-phone .btn,
531531
.compilation-pane #run-on-qemu .btn,
532532
.compilation-pane .build-buttons .btn {
533-
width: 137px;
533+
min-width: 137px;
534534
margin: 0 4px;
535+
wdith: initial;
535536
}
536537

537538
.compilation-pane #run-on-phone .btn:last-of-type,
@@ -684,6 +685,7 @@ table.build-results tr.pending .build-state {
684685

685686
.media-tool-buttons button {
686687
width: initial;
688+
min-width: 130px;
687689
margin-right: 10px;
688690
}
689691

ide/static/ide/js/live_settings_form.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ function make_live_settings_form(options) {
234234
init: function() {
235235
init();
236236
},
237-
save: function(element) {
238-
return save(element);
237+
save: function(element, event) {
238+
return save(element, event);
239239
}
240240
};
241241
}

ide/static/ide/js/published_media.js

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ CloudPebble.PublishedMedia = (function() {
2424
var pane;
2525
this.show_error = function show_error(error) {
2626
pane.find('.alert-error').removeClass('hide').text(error);
27+
$('#main-pane').animate({scrollTop: 0})
2728
};
2829
this.hide_error = function hide_error() {
2930
pane.find('.alert-error').addClass('hide');
@@ -59,7 +60,7 @@ CloudPebble.PublishedMedia = (function() {
5960
function MediaItem() {
6061
var self = this;
6162
var deleting = false;
62-
var item_form = media_template.find('form');
63+
var item_form = media_template.find('#media-items');
6364
var item = item_template.clone().appendTo(item_form).data('item', this);
6465
var name_input = item.find('.edit-media-name');
6566
var id_input = item.find('.edit-media-id');
@@ -226,10 +227,6 @@ CloudPebble.PublishedMedia = (function() {
226227
if (!_.every(names)) {
227228
throw new Error(gettext('Identifiers cannot be blank'));
228229
}
229-
// Check that all IDs are unique
230-
if (_.max(_.countBy(data, 'id')) > 1) {
231-
throw new Error(gettext('Numeric IDs must be unique'));
232-
}
233230
// Check that all identifiers are valid
234231
_.each(names, function(name) {
235232
if (!REGEXES.c_identifier.test(name)) {
@@ -238,33 +235,50 @@ CloudPebble.PublishedMedia = (function() {
238235
});
239236
return Ajax.Post('/ide/project/' + PROJECT_ID + '/save_published_media', {
240237
'published_media': JSON.stringify(data)
241-
}).then(function(result) {
242-
// TODO: use 'result' or not?
238+
}).then(function() {
243239
CloudPebble.ProjectInfo.published_media = data;
244240
sync_with_ycm();
245241
return null;
246242
});
247243
}
248244

249245
/** Save the whole form. If any names are incomplete or resources are invalid, it simply refuses to save without error. */
250-
function save_forms() {
251-
var items = get_media_items();
246+
function save_forms(event) {
252247
var data = get_form_data();
248+
var do_cancel = event.type != 'submit';
249+
var items = get_media_items();
253250
var identifiers = get_eligible_identifiers();
251+
function maybe_error(text) {
252+
if (do_cancel) return {incomplete: true};
253+
throw new Error(text);
254+
}
254255
// If not all items have names, cancel saving without displaying an error
255256
if (!_.every(_.pluck(data, 'name'))) {
256-
return {incomplete: true};
257+
return maybe_error(gettext("Published Media must have non-empty identifiers."))
258+
}
259+
260+
// Cancel if there are any incomplete items
261+
if (!_.every(_.map(data, function(item) {
262+
return !!item.timeline || !!item.glance;
263+
}))) {
264+
return maybe_error(gettext("Published Media items must specify glance, timeline icons, or both."))
257265
}
258-
// Raise an error if there are any invalid selections
266+
267+
// Check that all IDs are unique
268+
if (_.max(_.countBy(data, 'id')) > 1) {
269+
return maybe_error(gettext("Published Media IDs must be unique."))
270+
}
271+
272+
// Cancel (and show the 'invalid items' icon in the sidebar) if there are any invalid values
259273
var validity = _.map(items, function(item) {return item.is_valid(identifiers);});
260274
if (!_.every(validity)) {
261275
toggle_sidebar_error(true);
262-
return {incomplete: true};
276+
return maybe_error(gettext("You cannot save Published Media items with references to resuorces which do not exist."))
263277
}
264278

265-
// If we successfully saved, it implies that there were no invalid references
266-
// so we can get rid of the sidebar error notification.
267279
return save_pubished_media(data).then(function() {
280+
// If we successfully saved, it implies that there were no invalid references
281+
// so we can get rid of the sidebar error notification.
268282
toggle_sidebar_error(false);
269283
});
270284
}
@@ -274,12 +288,17 @@ CloudPebble.PublishedMedia = (function() {
274288
if (media_pane_setup) return false;
275289
media_pane_setup = true;
276290

277-
var initial_data = CloudPebble.ProjectInfo.published_media;
278-
_.each(initial_data, function(data) {
291+
// Set up the data
292+
_.each(CloudPebble.ProjectInfo.published_media, function(data) {
279293
var item = new MediaItem();
280294
item.setData(data);
281295
});
282296

297+
media_template.find('form').submit(function(e) {
298+
live_form.save(null, e);
299+
return false;
300+
});
301+
283302
media_template.find('#add-published-media').click(function() {
284303
new MediaItem();
285304
});
@@ -290,7 +309,7 @@ CloudPebble.PublishedMedia = (function() {
290309
error_function: alerts.show_error,
291310
on_progress_started: alerts.show_progress,
292311
on_progress_complete: alerts.hide_progress,
293-
form: media_template.find('form')
312+
form: media_template.find('#media-items')
294313
});
295314
media_template.find('.media-tool-buttons').removeClass('hide');
296315
media_template.find('.media-pane-loading').remove();

ide/templates/ide/project/published_media.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,23 @@
22
<!-- Compilation pane -->
33
<div id="media-pane-template" class="hide media-pane resource-pane">
44
<div class="well alert alert-error hide"></div>
5-
<form class="form-horizontal">
6-
</form>
5+
<form>
6+
<div id="media-items" class="form-horizontal">
77

8+
</div>
89
<div class="well media-pane-loading">
910
<h2>{% trans 'Loading...' %}</h2>
1011
</div>
1112

1213
<div class="well hide media-tool-buttons">
14+
<button type="submit" class="btn btn-affirmative" id="save-published-media">
15+
{% trans 'Save' %}
16+
</button>
1317
<button type="button" class="btn" id="add-published-media">
1418
{% trans 'Add New Published Media' %}
1519
</button>
1620
</div>
21+
</form>
1722
</div>
1823

1924
<div class="well hide media-item" id="media-item-template" >

0 commit comments

Comments
 (0)