From ad082cea700bafef449ad94a45a8db309fe7209a Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 14 Mar 2013 14:45:29 -0400 Subject: [PATCH] Big commit: - Support for a popup that shows similar topics - Cleaned up a lot of Javascript - Cleaned up use of Promises --- Gemfile.lock | 2 +- .../javascripts/admin/models/admin_user.js | 12 +- .../javascripts/admin/models/flagged_post.js | 38 +---- .../javascripts/admin/models/version_check.js | 10 +- .../controllers/composer_controller.js | 117 +++++++++++---- .../discourse/controllers/list_controller.js | 24 ++- .../discourse/controllers/topic_controller.js | 17 +-- .../discourse/models/action_summary.js | 19 +-- .../discourse/models/category_list.js | 14 +- .../javascripts/discourse/models/composer.js | 122 ++++++++------- .../javascripts/discourse/models/draft.js | 36 +---- .../discourse/models/invite_list.js | 33 ++-- .../javascripts/discourse/models/post.js | 53 +++---- .../javascripts/discourse/models/topic.js | 25 +++- .../discourse/models/topic_list.js | 74 ++++----- .../javascripts/discourse/models/user.js | 20 +-- .../templates/composer.js.handlebars | 16 +- .../discourse/views/composer_view.js | 141 +++++++----------- .../discourse/views/list/list_topics_view.js | 28 ++-- .../discourse/views/modal/history_view.js | 25 ++-- .../javascripts/discourse/views/post_view.js | 22 ++- .../javascripts/discourse/views/topic_view.js | 6 +- app/assets/javascripts/preload_store.js | 48 +++--- .../stylesheets/application/compose.css.scss | 15 +- app/controllers/topics_controller.rb | 11 ++ app/models/site_setting.rb | 2 + app/models/topic.rb | 17 ++- app/serializers/basic_topic_serializer.rb | 53 +------ app/serializers/category_topic_serializer.rb | 8 +- app/serializers/listable_topic_serializer.rb | 56 +++++++ app/serializers/suggested_topic_serializer.rb | 4 +- app/serializers/topic_list_item_serializer.rb | 5 +- config/locales/client.en.yml | 1 + config/locales/server.en.yml | 3 + config/routes.rb | 3 +- .../20130315180637_enable_trigram_support.rb | 9 ++ spec/controllers/topics_controller_spec.rb | 29 ++++ spec/javascripts/preload_store_spec.js | 8 +- spec/models/topic_spec.rb | 18 +++ 39 files changed, 584 insertions(+), 560 deletions(-) create mode 100644 app/serializers/listable_topic_serializer.rb create mode 100644 db/migrate/20130315180637_enable_trigram_support.rb diff --git a/Gemfile.lock b/Gemfile.lock index a01a5c033..df0055bc3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -456,7 +456,7 @@ GEM turbo-sprockets-rails3 (0.3.6) railties (> 3.2.8, < 4.0.0) sprockets (>= 2.0.0) - tzinfo (0.3.35) + tzinfo (0.3.37) uglifier (1.3.0) execjs (>= 0.3.0) multi_json (~> 1.0, >= 1.0.2) diff --git a/app/assets/javascripts/admin/models/admin_user.js b/app/assets/javascripts/admin/models/admin_user.js index ff2edf73d..47a111969 100644 --- a/app/assets/javascripts/admin/models/admin_user.js +++ b/app/assets/javascripts/admin/models/admin_user.js @@ -156,15 +156,9 @@ Discourse.AdminUser.reopenClass({ }, find: function(username) { - var promise; - promise = new RSVP.Promise(); - $.ajax({ - url: "/admin/users/" + username, - success: function(result) { - return promise.resolve(Discourse.AdminUser.create(result)); - } - }); - return promise; + return $.ajax({url: "/admin/users/" + username}).then(function (result) { + return Discourse.AdminUser.create(result); + }) }, findAll: function(query, filter) { diff --git a/app/assets/javascripts/admin/models/flagged_post.js b/app/assets/javascripts/admin/models/flagged_post.js index bab8ea0c3..e22e751d2 100644 --- a/app/assets/javascripts/admin/models/flagged_post.js +++ b/app/assets/javascripts/admin/models/flagged_post.js @@ -46,47 +46,15 @@ Discourse.FlaggedPost = Discourse.Post.extend({ }).property('topic_hidden'), deletePost: function() { - var promise; - promise = new RSVP.Promise(); if (this.get('post_number') === "1") { - return $.ajax("/t/" + this.topic_id, { - type: 'DELETE', - cache: false, - success: function() { - promise.resolve(); - }, - error: function(e) { - promise.reject(); - } - }); + return $.ajax("/t/" + this.topic_id, { type: 'DELETE', cache: false }); } else { - return $.ajax("/posts/" + this.id, { - type: 'DELETE', - cache: false, - success: function() { - promise.resolve(); - }, - error: function(e) { - promise.reject(); - } - }); + return $.ajax("/posts/" + this.id, { type: 'DELETE', cache: false }); } }, clearFlags: function() { - var promise; - promise = new RSVP.Promise(); - $.ajax("/admin/flags/clear/" + this.id, { - type: 'POST', - cache: false, - success: function() { - promise.resolve(); - }, - error: function(e) { - promise.reject(); - } - }); - return promise; + return $.ajax("/admin/flags/clear/" + this.id, { type: 'POST', cache: false }); }, hiddenClass: (function() { diff --git a/app/assets/javascripts/admin/models/version_check.js b/app/assets/javascripts/admin/models/version_check.js index 699784848..91640b86d 100644 --- a/app/assets/javascripts/admin/models/version_check.js +++ b/app/assets/javascripts/admin/models/version_check.js @@ -26,14 +26,8 @@ Discourse.VersionCheck = Discourse.Model.extend({ Discourse.VersionCheck.reopenClass({ find: function() { - var promise = new RSVP.Promise(); - $.ajax({ - url: '/admin/version_check', - dataType: 'json', - success: function(json) { - promise.resolve(Discourse.VersionCheck.create(json)); - } + return $.ajax({ url: '/admin/version_check', dataType: 'json' }).then(function(json) { + return Discourse.VersionCheck.create(json); }); - return promise; } }); \ No newline at end of file diff --git a/app/assets/javascripts/discourse/controllers/composer_controller.js b/app/assets/javascripts/discourse/controllers/composer_controller.js index 6ef3ad662..22cb510b0 100644 --- a/app/assets/javascripts/discourse/controllers/composer_controller.js +++ b/app/assets/javascripts/discourse/controllers/composer_controller.js @@ -8,22 +8,20 @@ **/ Discourse.ComposerController = Discourse.Controller.extend({ needs: ['modal', 'topic'], - hasReply: false, togglePreview: function() { - return this.get('content').togglePreview(); + this.get('content').togglePreview(); }, // Import a quote from the post importQuote: function() { - return this.get('content').importQuote(); + this.get('content').importQuote(); }, appendText: function(text) { - var c; - c = this.get('content'); + var c = this.get('content'); if (c) { - return c.appendText(text); + c.appendText(text); } }, @@ -72,11 +70,10 @@ Discourse.ComposerController = Discourse.Controller.extend({ } bootbox.dialog(message, buttons); - return; } } - + return composer.save({ imageSizes: this.get('view').imageSizes() }).then(function(opts) { @@ -94,17 +91,80 @@ Discourse.ComposerController = Discourse.Controller.extend({ }); }, - checkReplyLength: function() { - if (this.present('content.reply')) { - this.set('hasReply', true); - } else { - this.set('hasReply', false); + closeEducation: function() { + this.set('educationClosed', true); + }, + + closeSimilar: function() { + this.set('similarClosed', true); + }, + + similarVisible: function() { + if (this.get('similarClosed')) return false; + if (this.get('content.composeState') !== Discourse.Composer.OPEN) return false; + return (this.get('similarTopics.length') || 0) > 0; + }.property('similarTopics.length', 'similarClosed', 'content.composeState'), + + newUserEducationVisible: function() { + if (!this.get('educationContents')) return false; + if (this.get('content.composeState') !== Discourse.Composer.OPEN) return false; + if (!this.present('content.reply')) return false; + if (this.get('educationClosed')) return false; + return true; + }.property('content.composeState', 'content.reply', 'educationClosed', 'educationContents'), + + fetchNewUserEducation: function() { + // If creating a topic, use topic_count, otherwise post_count + var count = this.get('content.creatingTopic') ? Discourse.get('currentUser.topic_count') : Discourse.get('currentUser.reply_count'); + if (count >= Discourse.SiteSettings.educate_until_posts) { + this.set('educationClosed', true); + this.set('educationContents', ''); + return; } + + // The user must have typed a reply + if (!this.get('typedReply')) return; + + this.set('educationClosed', false); + + // If visible update the text + var educationKey = this.get('content.creatingTopic') ? 'new-topic' : 'new-reply'; + var composerController = this; + $.get("/education/" + educationKey).then(function(result) { + composerController.set('educationContents', result); + }); + }.observes('typedReply', 'content.creatingTopic', 'Discourse.currentUser.reply_count'), + + checkReplyLength: function() { + this.set('typedReply', this.present('content.reply')); + }, + + /** + Fired after a user stops typing. Considers whether to check for similar + topics based on the current composer state. + + @method findSimilarTopics + **/ + findSimilarTopics: function() { + + // We don't care about similar topics unless creating a topic + if (!this.get('content.creatingTopic')) return; + + var body = this.get('content.reply'); + var title = this.get('content.title'); + + // Ensure the fields are of the minimum length + if (body.length < Discourse.SiteSettings.min_body_similar_length) return; + if (title.length < Discourse.SiteSettings.min_title_similar_length) return; + + var composerController = this; + Discourse.Topic.findSimilarTo(title, body).then(function (topics) { + composerController.set('similarTopics', topics); + }); }, saveDraft: function() { - var model; - model = this.get('content'); + var model = this.get('content'); if (model) model.saveDraft(); }, @@ -123,8 +183,11 @@ Discourse.ComposerController = Discourse.Controller.extend({ _this = this; if (!opts) opts = {}; - opts.promise = promise = opts.promise || new RSVP.Promise(); - this.set('hasReply', false); + opts.promise = promise = opts.promise || Ember.Deferred.create(); + this.set('typedReply', false); + this.set('similarTopics', null); + this.set('similarClosed', false); + if (!opts.draftKey) { alert("composer was opened without a draft key"); throw "composer opened without a proper draft key"; @@ -133,9 +196,7 @@ Discourse.ComposerController = Discourse.Controller.extend({ // ensure we have a view now, without it transitions are going to be messed view = this.get('view'); if (!view) { - view = Discourse.ComposerView.create({ - controller: this - }); + view = Discourse.ComposerView.create({ controller: this }); view.appendTo($('#main')); this.set('view', view); // the next runloop is too soon, need to get the control rendered and then @@ -197,8 +258,7 @@ Discourse.ComposerController = Discourse.Controller.extend({ }, wouldLoseChanges: function() { - var composer; - composer = this.get('content'); + var composer = this.get('content'); return composer && composer.wouldLoseChanges(); }, @@ -210,10 +270,9 @@ Discourse.ComposerController = Discourse.Controller.extend({ }, destroyDraft: function() { - var key; - key = this.get('content.draftKey'); + var key = this.get('content.draftKey'); if (key) { - return Discourse.Draft.clear(key, this.get('content.draftSequence')); + Discourse.Draft.clear(key, this.get('content.draftSequence')); } }, @@ -243,17 +302,17 @@ Discourse.ComposerController = Discourse.Controller.extend({ } }, - click: function() { + openIfDraft: function() { if (this.get('content.composeState') === Discourse.Composer.DRAFT) { - return this.set('content.composeState', Discourse.Composer.OPEN); + this.set('content.composeState', Discourse.Composer.OPEN); } }, shrink: function() { if (this.get('content.reply') === this.get('content.originalText')) { - return this.close(); + this.close(); } else { - return this.collapse(); + this.collapse(); } }, diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js b/app/assets/javascripts/discourse/controllers/list_controller.js index 74f08b8eb..1725f6bdb 100644 --- a/app/assets/javascripts/discourse/controllers/list_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_controller.js @@ -42,13 +42,11 @@ Discourse.ListController = Discourse.Controller.extend({ this.set('loading', true); if (filterMode === 'categories') { - return Ember.Deferred.promise(function(deferred) { - Discourse.CategoryList.list(filterMode).then(function(items) { - listController.set('loading', false); - listController.set('filterMode', filterMode); - listController.set('categoryMode', true); - return deferred.resolve(items); - }); + return Discourse.CategoryList.list(filterMode).then(function(items) { + listController.set('loading', false); + listController.set('filterMode', filterMode); + listController.set('categoryMode', true); + return items; }); } @@ -56,13 +54,11 @@ Discourse.ListController = Discourse.Controller.extend({ if (!current) { current = Discourse.NavItem.create({ name: filterMode }); } - return Ember.Deferred.promise(function(deferred) { - Discourse.TopicList.list(current).then(function(items) { - listController.set('filterSummary', items.filter_summary); - listController.set('filterMode', filterMode); - listController.set('loading', false); - return deferred.resolve(items); - }); + return Discourse.TopicList.list(current).then(function(items) { + listController.set('filterSummary', items.filter_summary); + listController.set('filterMode', filterMode); + listController.set('loading', false); + return items; }); }, diff --git a/app/assets/javascripts/discourse/controllers/topic_controller.js b/app/assets/javascripts/discourse/controllers/topic_controller.js index dd8cac3a9..30e244c1e 100644 --- a/app/assets/javascripts/discourse/controllers/topic_controller.js +++ b/app/assets/javascripts/discourse/controllers/topic_controller.js @@ -117,19 +117,18 @@ Discourse.TopicController = Discourse.ObjectController.extend({ }, replyAsNewTopic: function(post) { - var composerController, postLink, postUrl, promise; - composerController = this.get('controllers.composer'); - // TODO shut down topic draft cleanly if it exists ... - promise = composerController.open({ + var composerController = this.get('controllers.composer'); + var promise = composerController.open({ action: Discourse.Composer.CREATE_TOPIC, draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY }); - postUrl = "" + location.protocol + "//" + location.host + (post.get('url')); - postLink = "[" + (this.get('title')) + "](" + postUrl + ")"; - return promise.then(function() { - return Discourse.Post.loadQuote(post.get('id')).then(function(q) { - return composerController.appendText("" + (Em.String.i18n("post.continue_discussion", { + var postUrl = "" + location.protocol + "//" + location.host + (post.get('url')); + var postLink = "[" + (this.get('title')) + "](" + postUrl + ")"; + + promise.then(function() { + Discourse.Post.loadQuote(post.get('id')).then(function(q) { + composerController.appendText("" + (Em.String.i18n("post.continue_discussion", { postLink: postLink })) + "\n\n" + q); }); diff --git a/app/assets/javascripts/discourse/models/action_summary.js b/app/assets/javascripts/discourse/models/action_summary.js index ed03b40d6..e084eb06d 100644 --- a/app/assets/javascripts/discourse/models/action_summary.js +++ b/app/assets/javascripts/discourse/models/action_summary.js @@ -40,8 +40,6 @@ Discourse.ActionSummary = Discourse.Model.extend({ act: function(opts) { // Mark it as acted - var promise, - _this = this; this.set('acted', true); this.set('count', this.get('count') + 1); this.set('can_act', false); @@ -53,26 +51,19 @@ Discourse.ActionSummary = Discourse.Model.extend({ } // Create our post action - promise = new RSVP.Promise(); - $.ajax({ + var actionSummary = this; + return $.ajax({ url: "/post_actions", type: 'POST', data: { id: this.get('post.id'), post_action_type_id: this.get('id'), message: (opts ? opts.message : void 0) || "" - }, - error: function(error) { - var errors; - _this.removeAction(); - errors = $.parseJSON(error.responseText).errors; - return promise.reject(errors); - }, - success: function() { - return promise.resolve(); } + }).then(null, function (error) { + actionSummary.removeAction(); + return $.parseJSON(error.responseText).errors; }); - return promise; }, // Undo this action diff --git a/app/assets/javascripts/discourse/models/category_list.js b/app/assets/javascripts/discourse/models/category_list.js index 0f4476da2..e13025181 100644 --- a/app/assets/javascripts/discourse/models/category_list.js +++ b/app/assets/javascripts/discourse/models/category_list.js @@ -31,18 +31,14 @@ Discourse.CategoryList.reopenClass({ }, list: function(filter) { - var promise, - _this = this; - promise = new RSVP.Promise(); - $.getJSON("/" + filter + ".json").then(function(result) { - var categoryList; - categoryList = Discourse.TopicList.create(); + var route = this; + return $.getJSON("/" + filter + ".json").then(function(result) { + var categoryList = Discourse.TopicList.create(); categoryList.set('can_create_category', result.category_list.can_create_category); - categoryList.set('categories', _this.categoriesFrom(result)); + categoryList.set('categories', route.categoriesFrom(result)); categoryList.set('loaded', true); - return promise.resolve(categoryList); + return categoryList; }); - return promise; } }); diff --git a/app/assets/javascripts/discourse/models/composer.js b/app/assets/javascripts/discourse/models/composer.js index e265bd92e..4faa88a86 100644 --- a/app/assets/javascripts/discourse/models/composer.js +++ b/app/assets/javascripts/discourse/models/composer.js @@ -85,9 +85,9 @@ Discourse.Composer = Discourse.Model.extend({ if (post) { this.set('loading', true); var composer = this; - return Discourse.Post.load(post.get('id'), function(result) { + Discourse.Post.load(post.get('id')).then(function(result) { composer.appendText(Discourse.BBCode.buildQuoteBBCode(post, result.get('raw'))); - return composer.set('loading', false); + composer.set('loading', false); }); } }, @@ -249,9 +249,10 @@ Discourse.Composer = Discourse.Model.extend({ this.set('reply', opts.reply || this.get("reply") || ""); if (opts.postId) { this.set('loading', true); - Discourse.Post.load(opts.postId, function(result) { + Discourse.Post.load(opts.postId).then(function(result) { + console.log(result); composer.set('post', result); - return composer.set('loading', false); + composer.set('loading', false); }); } @@ -259,7 +260,7 @@ Discourse.Composer = Discourse.Model.extend({ if (opts.action === EDIT && opts.post) { this.set('title', this.get('topic.title')); this.set('loading', true); - Discourse.Post.load(opts.post.get('id'), function(result) { + Discourse.Post.load(opts.post.get('id')).then(function(result) { composer.set('reply', result.get('raw')); composer.set('originalText', composer.get('reply')); composer.set('loading', false); @@ -285,7 +286,6 @@ Discourse.Composer = Discourse.Model.extend({ // When you edit a post editPost: function(opts) { - var promise = new RSVP.Promise(); var post = this.get('post'); var oldCooked = post.get('cooked'); var composer = this; @@ -304,40 +304,37 @@ Discourse.Composer = Discourse.Model.extend({ post.set('cooked', $('#wmd-preview').html()); this.set('composeState', CLOSED); + return Ember.Deferred.promise(function(promise) { + post.save(function(savedPost) { + var posts = composer.get('topic.posts'); - post.save(function(savedPost) { - - var idx, postNumber; - var posts = composer.get('topic.posts'); - - // perhaps our post came from elsewhere eg. draft - idx = -1; - postNumber = post.get('post_number'); - posts.each(function(p, i) { - if (p.get('post_number') === postNumber) { - idx = i; + // perhaps our post came from elsewhere eg. draft + var idx = -1; + var postNumber = post.get('post_number'); + posts.each(function(p, i) { + if (p.get('post_number') === postNumber) { + idx = i; + } + }); + if (idx > -1) { + savedPost.set('topic', composer.get('topic')); + posts.replace(idx, 1, [savedPost]); + promise.resolve({ post: post }); + composer.set('topic.draft_sequence', savedPost.draft_sequence); } + }, function(error) { + var errors; + errors = $.parseJSON(error.responseText).errors; + promise.reject(errors[0]); + post.set('cooked', oldCooked); + return composer.set('composeState', OPEN); }); - if (idx > -1) { - savedPost.set('topic', composer.get('topic')); - posts.replace(idx, 1, [savedPost]); - promise.resolve({ post: post }); - composer.set('topic.draft_sequence', savedPost.draft_sequence); - } - }, function(error) { - var errors; - errors = $.parseJSON(error.responseText).errors; - promise.reject(errors[0]); - post.set('cooked', oldCooked); - return composer.set('composeState', OPEN); }); - return promise; }, // Create a new Post createPost: function(opts) { - var promise = new RSVP.Promise(), - post = this.get('post'), + var post = this.get('post'), topic = this.get('topic'), currentUser = Discourse.get('currentUser'), addedToStream = false; @@ -401,38 +398,37 @@ Discourse.Composer = Discourse.Model.extend({ // Save callback var composer = this; - createdPost.save(function(result) { - var addedPost = false, - saving = true; - createdPost.updateFromSave(result); - if (topic) { - // It's no longer a new post - createdPost.set('newPost', false); - topic.set('draft_sequence', result.draft_sequence); - } else { - // We created a new topic, let's show it. - composer.set('composeState', CLOSED); - saving = false; - } - composer.set('reply', ''); - composer.set('createdPost', createdPost); - if (addedToStream) { - composer.set('composeState', CLOSED); - } else if (saving) { - composer.set('composeState', SAVING); - } - return promise.resolve({ post: result }); - }, function(error) { - // If an error occurs - var errors; - if (topic) { - topic.posts.removeObject(createdPost); - } - errors = $.parseJSON(error.responseText).errors; - promise.reject(errors[0]); - composer.set('composeState', OPEN); + return Ember.Deferred.promise(function(promise) { + createdPost.save(function(result) { + var addedPost = false, + saving = true; + createdPost.updateFromSave(result); + if (topic) { + // It's no longer a new post + createdPost.set('newPost', false); + topic.set('draft_sequence', result.draft_sequence); + } else { + // We created a new topic, let's show it. + composer.set('composeState', CLOSED); + saving = false; + } + composer.set('reply', ''); + composer.set('createdPost', createdPost); + if (addedToStream) { + composer.set('composeState', CLOSED); + } else if (saving) { + composer.set('composeState', SAVING); + } + return promise.resolve({ post: result }); + }, function(error) { + // If an error occurs + if (topic) { + topic.posts.removeObject(createdPost); + } + promise.reject($.parseJSON(error.responseText).errors[0]); + composer.set('composeState', OPEN); + }); }); - return promise; }, saveDraft: function() { diff --git a/app/assets/javascripts/discourse/models/draft.js b/app/assets/javascripts/discourse/models/draft.js index 17bd0402b..8309ffb02 100644 --- a/app/assets/javascripts/discourse/models/draft.js +++ b/app/assets/javascripts/discourse/models/draft.js @@ -22,20 +22,11 @@ Discourse.Draft.reopenClass({ }, get: function(key) { - var promise, - _this = this; - promise = new RSVP.Promise(); - $.ajax({ + return $.ajax({ url: '/draft', - data: { - draft_key: key - }, - dataType: 'json', - success: function(data) { - return promise.resolve(data); - } + data: { draft_key: key }, + dataType: 'json' }); - return promise; }, getLocal: function(key, current) { @@ -44,35 +35,16 @@ Discourse.Draft.reopenClass({ }, save: function(key, sequence, data) { - var promise; - promise = new RSVP.Promise(); data = typeof data === "string" ? data : JSON.stringify(data); - $.ajax({ + return $.ajax({ type: 'POST', url: "/draft", data: { draft_key: key, data: data, sequence: sequence - }, - success: function() { - /* don't keep local - */ - - /* Discourse.KeyValueStore.remove("draft_#{key}") - */ - return promise.resolve(); - }, - error: function() { - /* save local - */ - - /* Discourse.KeyValueStore.set(key: "draft_#{key}", value: data) - */ - return promise.reject(); } }); - return promise; } }); diff --git a/app/assets/javascripts/discourse/models/invite_list.js b/app/assets/javascripts/discourse/models/invite_list.js index 988817df2..4e349031b 100644 --- a/app/assets/javascripts/discourse/models/invite_list.js +++ b/app/assets/javascripts/discourse/models/invite_list.js @@ -15,28 +15,21 @@ Discourse.InviteList = Discourse.Model.extend({ Discourse.InviteList.reopenClass({ findInvitedBy: function(user) { - var promise; - promise = new RSVP.Promise(); - $.ajax({ - url: "/users/" + (user.get('username_lower')) + "/invited.json", - success: function(result) { - var invitedList; - invitedList = result.invited_list; - if (invitedList.pending) { - invitedList.pending = invitedList.pending.map(function(i) { - return Discourse.Invite.create(i); - }); - } - if (invitedList.redeemed) { - invitedList.redeemed = invitedList.redeemed.map(function(i) { - return Discourse.Invite.create(i); - }); - } - invitedList.user = user; - return promise.resolve(Discourse.InviteList.create(invitedList)); + return $.ajax({ url: "/users/" + (user.get('username_lower')) + "/invited.json" }).then(function (result) { + var invitedList = result.invited_list; + if (invitedList.pending) { + invitedList.pending = invitedList.pending.map(function(i) { + return Discourse.Invite.create(i); + }); } + if (invitedList.redeemed) { + invitedList.redeemed = invitedList.redeemed.map(function(i) { + return Discourse.Invite.create(i); + }); + } + invitedList.user = user; + return Discourse.InviteList.create(invitedList); }); - return promise; } }); diff --git a/app/assets/javascripts/discourse/models/post.js b/app/assets/javascripts/discourse/models/post.js index 3742c8f86..0b48073f0 100644 --- a/app/assets/javascripts/discourse/models/post.js +++ b/app/assets/javascripts/discourse/models/post.js @@ -215,22 +215,19 @@ Discourse.Post = Discourse.Model.extend({ // Load replies to this post loadReplies: function() { - var promise, - _this = this; - promise = new RSVP.Promise(); this.set('loadingReplies', true); this.set('replies', []); - $.getJSON("/posts/" + (this.get('id')) + "/replies", function(loaded) { + + var parent = this; + return $.ajax({url: "/posts/" + (this.get('id')) + "/replies"}).then(function(loaded) { + var replies = parent.get('replies'); loaded.each(function(reply) { - var post; - post = Discourse.Post.create(reply); - post.set('topic', _this.get('topic')); - return _this.get('replies').pushObject(post); + var post = Discourse.Post.create(reply); + post.set('topic', parent.get('topic')); + replies.pushObject(post); }); - _this.set('loadingReplies', false); - return promise.resolve(); + parent.set('loadingReplies', false); }); - return promise; }, loadVersions: function(callback) { @@ -293,43 +290,33 @@ Discourse.Post.reopenClass({ return $.ajax("/posts/destroy_many", { type: 'DELETE', data: { - post_ids: posts.map(function(p) { - return p.get('id'); - }) + post_ids: posts.map(function(p) { return p.get('id'); }) } }); }, loadVersion: function(postId, version, callback) { - var _this = this; - return $.getJSON("/posts/" + postId + ".json?version=" + version, function(result) { - return callback(Discourse.Post.create(result)); + return $.ajax({url: "/posts/" + postId + ".json?version=" + version}).then(function(result) { + return Discourse.Post.create(result); }); }, - loadByPostNumber: function(topicId, postId, callback) { - var _this = this; - return $.getJSON("/posts/by_number/" + topicId + "/" + postId + ".json", function(result) { - return callback(Discourse.Post.create(result)); + loadByPostNumber: function(topicId, postId) { + return $.ajax({url: "/posts/by_number/" + topicId + "/" + postId + ".json"}).then(function (result) { + return Discourse.Post.create(result); }); }, loadQuote: function(postId) { - var promise, - _this = this; - promise = new RSVP.Promise(); - $.getJSON("/posts/" + postId + ".json", function(result) { - var post; - post = Discourse.Post.create(result); - return promise.resolve(Discourse.BBCode.buildQuoteBBCode(post, post.get('raw'))); + return $.ajax({url: "/posts/" + postId + ".json"}).then(function(result) { + var post = Discourse.Post.create(result); + return Discourse.BBCode.buildQuoteBBCode(post, post.get('raw')); }); - return promise; }, - load: function(postId, callback) { - var _this = this; - return $.getJSON("/posts/" + postId + ".json", function(result) { - return callback(Discourse.Post.create(result)); + load: function(postId) { + return $.ajax({url: "/posts/" + postId + ".json"}).then(function (result) { + return Discourse.Post.create(result); }); } diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index 937ab5824..0f02ec12f 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -372,6 +372,20 @@ Discourse.Topic.reopenClass({ MUTE: 0 }, + /** + Find similar topics to a given title and body + + @method findSimilar + @param {String} title The current title + @param {String} body The current body + @returns A promise that will resolve to the topics + **/ + findSimilarTo: function(title, body) { + return $.ajax({url: "/topics/similar_to", data: {title: title, raw: body} }).then(function (results) { + return results.map(function(topic) { return Discourse.Topic.create(topic) }); + }); + }, + // Load a topic, but accepts a set of filters // options: // onLoad - the callback after the topic is loaded @@ -408,20 +422,15 @@ Discourse.Topic.reopenClass({ } // Check the preload store. If not, load it via JSON - promise = new RSVP.Promise(); - PreloadStore.get("topic_" + topicId, function() { + return PreloadStore.get("topic_" + topicId, function() { return $.getJSON(url + ".json", data); }).then(function(result) { - var first; - first = result.posts.first(); + var first = result.posts.first(); if (first && opts && opts.bestOf) { first.bestOfFirst = true; } - return promise.resolve(result); - }, function(result) { - return promise.reject(result); + return result; }); - return promise; }, // Create a topic from posts diff --git a/app/assets/javascripts/discourse/models/topic_list.js b/app/assets/javascripts/discourse/models/topic_list.js index 2de6cd5c1..713da8ab8 100644 --- a/app/assets/javascripts/discourse/models/topic_list.js +++ b/app/assets/javascripts/discourse/models/topic_list.js @@ -10,42 +10,38 @@ Discourse.TopicList = Discourse.Model.extend({ loadMoreTopics: function() { - var moreUrl, promise, - _this = this; - promise = new RSVP.Promise(); + var moreUrl, _this = this; + if (moreUrl = this.get('more_topics_url')) { Discourse.URL.replaceState("/" + (this.get('filter')) + "/more"); - $.ajax(moreUrl, { - success: function(result) { - var newTopics, topicIds, topics, topicsAdded = 0; - if (result) { - // the new topics loaded from the server - newTopics = Discourse.TopicList.topicsFrom(result); - // the current topics - topics = _this.get('topics'); - // keeps track of the ids of the current topics - topicIds = []; - topics.each(function(t) { - topicIds[t.get('id')] = true; - }); - // add new topics to the list of current topics if not already present - newTopics.each(function(t) { - if (!topicIds[t.get('id')]) { - // highlight the first of the new topics so we can get a visual feedback - t.set('highlight', topicsAdded++ === 0); - return topics.pushObject(t); - } - }); - _this.set('more_topics_url', result.topic_list.more_topics_url); - Discourse.set('transient.topicsList', _this); - } - return promise.resolve(result.topic_list.more_topics_url ? true : false); + return $.ajax({url: moreUrl}).then(function (result) { + var newTopics, topicIds, topics, topicsAdded = 0; + if (result) { + // the new topics loaded from the server + newTopics = Discourse.TopicList.topicsFrom(result); + // the current topics + topics = _this.get('topics'); + // keeps track of the ids of the current topics + topicIds = []; + topics.each(function(t) { + topicIds[t.get('id')] = true; + }); + // add new topics to the list of current topics if not already present + newTopics.each(function(t) { + if (!topicIds[t.get('id')]) { + // highlight the first of the new topics so we can get a visual feedback + t.set('highlight', topicsAdded++ === 0); + return topics.pushObject(t); + } + }); + _this.set('more_topics_url', result.topic_list.more_topics_url); + Discourse.set('transient.topicsList', _this); } + return result.topic_list.more_topics_url; }); } else { - promise.resolve(false); + return null; } - return promise; }, insert: function(json) { @@ -90,7 +86,7 @@ Discourse.TopicList.reopenClass({ }, list: function(menuItem) { - var filter, found, list, promise, topic_list, url; + var filter, list, promise, topic_list, url; filter = menuItem.name; topic_list = Discourse.TopicList.create(); topic_list.set('inserted', Em.A()); @@ -101,19 +97,16 @@ Discourse.TopicList.reopenClass({ } if (list = Discourse.get('transient.topicsList')) { if ((list.get('filter') === filter) && window.location.pathname.indexOf('more') > 0) { - promise = new RSVP.Promise(); list.set('loaded', true); - promise.resolve(list); - return promise; + return Ember.Deferred.promise(function(promise) { + promise.resolve(list); + }); } } Discourse.set('transient.topicsList', null); Discourse.set('transient.topicListScrollPos', null); - promise = new RSVP.Promise(); - found = PreloadStore.contains('topic_list'); - PreloadStore.get("topic_list", function() { - return $.getJSON(url); - }).then(function(result) { + + return PreloadStore.get("topic_list", function() { return $.getJSON(url) }).then(function(result) { topic_list.set('topics', Discourse.TopicList.topicsFrom(result)); topic_list.set('can_create_topic', result.topic_list.can_create_topic); topic_list.set('more_topics_url', result.topic_list.more_topics_url); @@ -125,9 +118,8 @@ Discourse.TopicList.reopenClass({ topic_list.set('category', Discourse.Category.create(result.topic_list.filtered_category)); } topic_list.set('loaded', true); - return promise.resolve(topic_list); + return topic_list; }); - return promise; } }); diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index 0fe249319..509d54fe6 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -18,7 +18,7 @@ Discourse.User = Discourse.Model.extend({ return Discourse.Utilities.avatarUrl(this.get('username'), 'large', this.get('avatar_template')); }).property('username'), - /** + /** Small version of this user's avatar. @property avatarSmall @@ -68,7 +68,7 @@ Discourse.User = Discourse.Model.extend({ return Discourse.get('site.trust_levels').findProperty('id', this.get('trust_level')); }).property('trust_level'), - /** + /** Changes this user's username. @method changeUsername @@ -76,7 +76,7 @@ Discourse.User = Discourse.Model.extend({ @returns Result of ajax call **/ changeUsername: function(newUsername) { - return jQuery.ajax({ + return $.ajax({ url: "/users/" + (this.get('username_lower')) + "/preferences/username", type: 'PUT', data: { @@ -93,7 +93,7 @@ Discourse.User = Discourse.Model.extend({ @returns Result of ajax call **/ changeEmail: function(email) { - return jQuery.ajax({ + return $.ajax({ url: "/users/" + (this.get('username_lower')) + "/preferences/email", type: 'PUT', data: { @@ -121,7 +121,7 @@ Discourse.User = Discourse.Model.extend({ **/ save: function(finished) { var _this = this; - jQuery.ajax("/users/" + this.get('username').toLowerCase(), { + $.ajax("/users/" + this.get('username').toLowerCase(), { data: this.getProperties('auto_track_topics_after_msecs', 'bio_raw', 'website', @@ -153,7 +153,7 @@ Discourse.User = Discourse.Model.extend({ changePassword: function(callback) { var good; good = false; - jQuery.ajax({ + $.ajax({ url: '/session/forgot_password', dataType: 'json', data: { @@ -199,7 +199,7 @@ Discourse.User = Discourse.Model.extend({ var stream, _this = this; stream = this.get('stream'); - jQuery.ajax({ + $.ajax({ url: "/user_actions/" + id + ".json", dataType: 'json', cache: 'false', @@ -241,7 +241,7 @@ Discourse.User = Discourse.Model.extend({ url += "&filter=" + (this.get('streamFilter')); } - return jQuery.ajax({ + return $.ajax({ url: url, dataType: 'json', cache: 'false', @@ -362,7 +362,7 @@ Discourse.User.reopenClass({ @param {String} email An email address to check **/ checkUsername: function(username, email) { - return jQuery.ajax({ + return $.ajax({ url: '/users/check_username', type: 'GET', data: { @@ -465,7 +465,7 @@ Discourse.User.reopenClass({ @returns Result of ajax call **/ createAccount: function(name, email, password, username, passwordConfirm, challenge) { - return jQuery.ajax({ + return $.ajax({ url: '/users', dataType: 'json', data: { diff --git a/app/assets/javascripts/discourse/templates/composer.js.handlebars b/app/assets/javascripts/discourse/templates/composer.js.handlebars index 55180cf3d..fc386e2d3 100644 --- a/app/assets/javascripts/discourse/templates/composer.js.handlebars +++ b/app/assets/javascripts/discourse/templates/composer.js.handlebars @@ -2,10 +2,20 @@
-