Slide 126
Slide 126 text
App.GfNewPostComponent = Ember.Component.extend({
/* PROPERTIES */
layoutName: "components/gf-new-post",
gifPost: null,
classNames: ["new-post-component"],
classNameBindings: ["formState", "isInvalid"],
formState: "initial",
// FORM-STATES: "initial", "editing", "loading", "failure", "success"
isValid: Ember.computed.alias("gifPost.isValid"),
isInvalid: Ember.computed.not("isValid"),
/* OBSERVERS */
// This computes the displayed message for all success/failure
message: function(){
var formState = this.get("formState")
// On failure, delegate message to the object
if (formState === "failure") {
return this.get("gifPost.message");
// On success, just scream real loud!
} else if (formState === "success") {
return "New gif posted: " + this.get("gifPost.parsedUrl")
// If it's invalid but has a gif, it's too long:
} else if (this.get("isInvalid") && !!this.get("gifPost.isGif")) {
return "Your message is too long.";
// Otherwise if it's not valid it's not a gif
} else if (this.get("isInvalid")) {
return "Please add a valid gif link to this post.";
} else {
return null;
}
}.property("formState", "isInvalid", "gifPost.isGif", "gifPost.message"),
/* ACTIONS */
// These actions are primarily about pushing state around.
actions: {
showDialog: function() {
this.set("formState", "editing");
},
cancel: function() {
this.set("formState", "initial");
this.set("gifPost", App.store.createRecord("gifPost"));
},
submit: function() {
controller = this;
controller.set("formState", "loading");
var gifPost = this.get("gifPost");
gifPost.save().then(function(data) {
// Success
controller.set("formState", "success");
controller.defer(function() {
controller.set("formState", "initial")
}, 5000);
// Failure
}, function(data) {
controller.set("formState", "failure");
if (!!data.jqXHR && data.jqXHR.status == 422) {
// 422: validation error
controller.get("gifPost").set("message", data.jqXHR.responseJSON.errors.url[0]);
} else {
controller.get("gifPost").set("message", "There was an error posting your gif. Please wait and try again.")
}
controller.defer(function() {
controller.set("formState", "editing")
}, 5000);
});
}
},
/* FUNCTIONS */
// Allow injectable setTimeout override for testing
defer: function(callback, delay) {
setTimeout(callback, delay);
}
});
/* INITIALIZATION */
$(document).ready(function() {
$("#new-post-container").each(function(){
var component = App.GfNewPostComponent.create({
gifPost: App.store.createRecord("gifPost")
});
component.replaceIn(this);
});
});
App.GfListPostsComponent = Ember.Component.extend({
/* PROPERTIES */
layoutName: "components/gf-list-posts",
gifPosts: null,
classNames: ["list-posts-component"],
// We only want to show posts that are saved to the server
persistedGifPosts: Ember.computed.filterBy("gifPosts", "isNew", false),
sortedPosts: function() {
// well this is a neat trick, sort in reverse ID order
return this.get("persistedGifPosts").sortBy("id:desc");
}.property("persistedGifPosts.@each"),
/* ACTIONS */
actions: {
delete: function(gifPost) {
if (confirm("Really delete this lovely gif?")) {
gifPost.destroyRecord().then(function(result){
//message?
}, function(){
gifPost.rollback();
});
}
}
}
});
/* INITIALIZATION */
$(document).ready(function() {
if ($("#gif-posts-container").length) {
App.store.find("gifPost").then(function(result) {
$("#gif-posts-container").each(function(){
var component = App.GfListPostsComponent.create({
gifPosts: result
});
component.replaceIn(this);
});
});
}
});
App.GifPost = DS.Model.extend({
/* PROPERTIES */
body: DS.attr("string"),
url: DS.attr("string"),
username: DS.attr("string"),
message: null,
parsedUrl: function() {
if (!!this.get("body")) {
var matches = this.get("body").match(this.get("regex"))
return (matches && matches.length > 0) ? matches[0] : "";
}
}.property("body"),
isGif: function() {
return /.gif$/.test(this.get("parsedUrl"))
}.property("parsedUrl"),
charCount: function() {
if (this.get("isGif")) {
return this.get("body.length") - this.get("parsedUrl.length")
} else {
return this.get("body.length")
}
}.property("body", "parsedUrl", "isGif"),
isValid: function() {
return !!(this.get("charCount") <= 140 && this.get("isGif"))
}.property("charCount", "isGif"),
permalink: function() {
return "/gif_posts/" + this.get("id");
}.property("id"),
/* OBSERVERS */
// Copy the computed parsedUrl into the canonical url to send to the server
setUrl: function() {
this.set("url", this.get("parsedUrl"));
}.observes("parsedUrl"),
/* MISC */
// This property is at the end basically because it breaks Emacs auto-indent :/
regex: function() {
return /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/
}.property()
});
after