FIX: Highlighting was not being applied after some rendering.
Also includes a bunch of ES6 stuff.
This commit is contained in:
parent
96697c7957
commit
a519fd5bcf
|
@ -1,6 +1,7 @@
|
|||
import ObjectController from 'discourse/controllers/object';
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import { spinnerHTML } from 'discourse/helpers/loading-spinner';
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedContent, {
|
||||
multiSelect: false,
|
||||
|
@ -272,7 +273,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
|
|||
var self = this,
|
||||
props = this.get('buffered.buffer');
|
||||
|
||||
Discourse.Topic.update(this.get('model'), props).then(function() {
|
||||
Topic.update(this.get('model'), props).then(function() {
|
||||
// Note we roll back on success here because `update` saves
|
||||
// the properties to the topic.
|
||||
self.rollbackBuffer();
|
||||
|
@ -555,13 +556,13 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
|
|||
},
|
||||
|
||||
// Receive notifications for this topic
|
||||
subscribe: function() {
|
||||
subscribe() {
|
||||
// Unsubscribe before subscribing again
|
||||
this.unsubscribe();
|
||||
|
||||
var topicController = this;
|
||||
const self = this;
|
||||
Discourse.MessageBus.subscribe("/topic/" + this.get('id'), function(data) {
|
||||
var topic = topicController.get('model');
|
||||
const topic = self.get('model');
|
||||
|
||||
if (data.notification_level_change) {
|
||||
topic.set('details.notification_level', data.notification_level_change);
|
||||
|
@ -569,7 +570,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
|
|||
return;
|
||||
}
|
||||
|
||||
var postStream = topicController.get('postStream');
|
||||
const postStream = self.get('postStream');
|
||||
switch (data.type) {
|
||||
case "revised":
|
||||
case "acted":
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { decorateCooked } from 'discourse/lib/plugin-api';
|
||||
import HighlightSyntax from 'discourse/lib/highlight-syntax';
|
||||
import Lightbox from 'discourse/lib/lightbox';
|
||||
|
||||
export default {
|
||||
name: "post-decorations",
|
||||
initialize: function(container) {
|
||||
decorateCooked(container, HighlightSyntax);
|
||||
decorateCooked(container, Lightbox);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*global hljs:true */
|
||||
|
||||
export default function highlightSyntax($elem) {
|
||||
const selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]';
|
||||
$(selector, $elem).each(function(i, e) {
|
||||
return $LAB.script("/javascripts/highlight.pack.js").wait(function() {
|
||||
return hljs.highlightBlock(e);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/**
|
||||
Helper object for lightboxes.
|
||||
|
||||
@class Lightbox
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.Lightbox = {
|
||||
apply: function($elem) {
|
||||
$LAB.script("/javascripts/jquery.magnific-popup-min.js").wait(function() {
|
||||
$("a.lightbox", $elem).each(function(i, e) {
|
||||
var $e = $(e);
|
||||
// do not lightbox spoiled images
|
||||
if ($e.parents(".spoiler").length > 0 || $e.parents(".spoiled").length > 0) { return; }
|
||||
|
||||
$e.magnificPopup({
|
||||
type: "image",
|
||||
closeOnContentClick: false,
|
||||
removalDelay: 300,
|
||||
mainClass: "mfp-zoom-in",
|
||||
|
||||
callbacks: {
|
||||
open: function() {
|
||||
var wrap = this.wrap,
|
||||
img = this.currItem.img,
|
||||
maxHeight = img.css("max-height");
|
||||
|
||||
wrap.on("click.pinhandler", "img", function() {
|
||||
wrap.toggleClass("mfp-force-scrollbars");
|
||||
img.css("max-height", wrap.hasClass("mfp-force-scrollbars") ? "none" : maxHeight);
|
||||
});
|
||||
},
|
||||
beforeClose: function() {
|
||||
this.wrap.off("click.pinhandler");
|
||||
this.wrap.removeClass("mfp-force-scrollbars");
|
||||
}
|
||||
},
|
||||
|
||||
image: {
|
||||
titleSrc: function(item) {
|
||||
var href = item.el.data("download-href") || item.src;
|
||||
return [
|
||||
item.el.attr("title"),
|
||||
$("span.informations", item.el).text().replace('x', '×'),
|
||||
'<a class="image-source-link" href="' + href + '">' + I18n.t("lightbox.download") + '</a>'
|
||||
].join(' · ');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
export default function($elem) {
|
||||
$("a.lightbox", $elem).each(function(i, e) {
|
||||
$LAB.script("/javascripts/jquery.magnific-popup-min.js").wait(function() {
|
||||
var $e = $(e);
|
||||
// do not lightbox spoiled images
|
||||
if ($e.parents(".spoiler").length > 0 || $e.parents(".spoiled").length > 0) { return; }
|
||||
|
||||
$e.magnificPopup({
|
||||
type: "image",
|
||||
closeOnContentClick: false,
|
||||
removalDelay: 300,
|
||||
mainClass: "mfp-zoom-in",
|
||||
|
||||
callbacks: {
|
||||
open: function() {
|
||||
var wrap = this.wrap,
|
||||
img = this.currItem.img,
|
||||
maxHeight = img.css("max-height");
|
||||
|
||||
wrap.on("click.pinhandler", "img", function() {
|
||||
wrap.toggleClass("mfp-force-scrollbars");
|
||||
img.css("max-height", wrap.hasClass("mfp-force-scrollbars") ? "none" : maxHeight);
|
||||
});
|
||||
},
|
||||
beforeClose: function() {
|
||||
this.wrap.off("click.pinhandler");
|
||||
this.wrap.removeClass("mfp-force-scrollbars");
|
||||
}
|
||||
},
|
||||
|
||||
image: {
|
||||
titleSrc: function(item) {
|
||||
var href = item.el.data("download-href") || item.src;
|
||||
return [
|
||||
item.el.attr("title"),
|
||||
$("span.informations", item.el).text().replace('x', '×'),
|
||||
'<a class="image-source-link" href="' + href + '">' + I18n.t("lightbox.download") + '</a>'
|
||||
].join(' · ');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
export default function searchForTerm(term, opts) {
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
function searchForTerm(term, opts) {
|
||||
if (!opts) opts = {};
|
||||
|
||||
// Only include the data we have
|
||||
|
@ -22,7 +24,7 @@ export default function searchForTerm(term, opts) {
|
|||
|
||||
var topicMap = {};
|
||||
results.topics = results.topics.map(function(topic){
|
||||
topic = Discourse.Topic.create(topic);
|
||||
topic = Topic.create(topic);
|
||||
topicMap[topic.id] = topic;
|
||||
return topic;
|
||||
});
|
||||
|
@ -66,3 +68,5 @@ export default function searchForTerm(term, opts) {
|
|||
return noResults ? null : Em.Object.create(results);
|
||||
});
|
||||
}
|
||||
|
||||
export default searchForTerm;
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/*global hljs:true */
|
||||
|
||||
/**
|
||||
Helper object for syntax highlighting. Uses highlight.js which is loaded on demand.
|
||||
|
||||
@class SyntaxHighlighting
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.SyntaxHighlighting = {
|
||||
|
||||
/**
|
||||
Apply syntax highlighting to a jQuery element
|
||||
|
||||
@method apply
|
||||
@param {jQuery.selector} $elem The element we want to apply our highlighting to
|
||||
**/
|
||||
apply: function($elem) {
|
||||
var selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]';
|
||||
$(selector, $elem).each(function(i, e) {
|
||||
return $LAB.script("/javascripts/highlight.pack.js").wait(function() {
|
||||
return hljs.highlightBlock(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
|
@ -532,6 +532,7 @@ Discourse.Composer = Discourse.Model.extend({
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
// If we're in a topic, we can append the post instantly.
|
||||
if (postStream) {
|
||||
// If it's in reply to another post, increase the reply count
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
Discourse.PostStream = Em.Object.extend({
|
||||
|
||||
const PostStream = Ember.Object.extend({
|
||||
loading: Em.computed.or('loadingAbove', 'loadingBelow', 'loadingFilter', 'stagingPost'),
|
||||
notLoading: Em.computed.not('loading'),
|
||||
filteredPostsCount: Em.computed.alias("stream.length"),
|
||||
|
@ -45,15 +44,13 @@ Discourse.PostStream = Em.Object.extend({
|
|||
/**
|
||||
Returns a JS Object of current stream filter options. It should match the query
|
||||
params for the stream.
|
||||
|
||||
@property streamFilters
|
||||
**/
|
||||
streamFilters: function() {
|
||||
var result = {};
|
||||
const result = {};
|
||||
if (this.get('summary')) { result.filter = "summary"; }
|
||||
if (this.get('show_deleted')) { result.show_deleted = true; }
|
||||
|
||||
var userFilters = this.get('userFilters');
|
||||
const userFilters = this.get('userFilters');
|
||||
if (!Em.isEmpty(userFilters)) {
|
||||
result.username_filters = userFilters.join(",");
|
||||
}
|
||||
|
@ -62,27 +59,25 @@ Discourse.PostStream = Em.Object.extend({
|
|||
}.property('userFilters.[]', 'summary', 'show_deleted'),
|
||||
|
||||
hasNoFilters: function() {
|
||||
var streamFilters = this.get('streamFilters');
|
||||
const streamFilters = this.get('streamFilters');
|
||||
return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.username_filters));
|
||||
}.property('streamFilters.[]', 'topic.posts_count', 'posts.length'),
|
||||
|
||||
/**
|
||||
Returns the window of posts above the current set in the stream, bound to the top of the stream.
|
||||
This is the collection we'll ask for when scrolling upwards.
|
||||
|
||||
@property previousWindow
|
||||
**/
|
||||
previousWindow: function() {
|
||||
// If we can't find the last post loaded, bail
|
||||
var firstPost = _.first(this.get('posts'));
|
||||
const firstPost = _.first(this.get('posts'));
|
||||
if (!firstPost) { return []; }
|
||||
|
||||
// Find the index of the last post loaded, if not found, bail
|
||||
var stream = this.get('stream');
|
||||
var firstIndex = this.indexOf(firstPost);
|
||||
const stream = this.get('stream');
|
||||
const firstIndex = this.indexOf(firstPost);
|
||||
if (firstIndex === -1) { return []; }
|
||||
|
||||
var startIndex = firstIndex - this.get('topic.chunk_size');
|
||||
let startIndex = firstIndex - this.get('topic.chunk_size');
|
||||
if (startIndex < 0) { startIndex = 0; }
|
||||
return stream.slice(startIndex, firstIndex);
|
||||
|
||||
|
@ -91,17 +86,15 @@ Discourse.PostStream = Em.Object.extend({
|
|||
/**
|
||||
Returns the window of posts below the current set in the stream, bound by the bottom of the
|
||||
stream. This is the collection we use when scrolling downwards.
|
||||
|
||||
@property nextWindow
|
||||
**/
|
||||
nextWindow: function() {
|
||||
// If we can't find the last post loaded, bail
|
||||
var lastLoadedPost = this.get('lastLoadedPost');
|
||||
const lastLoadedPost = this.get('lastLoadedPost');
|
||||
if (!lastLoadedPost) { return []; }
|
||||
|
||||
// Find the index of the last post loaded, if not found, bail
|
||||
var stream = this.get('stream');
|
||||
var lastIndex = this.indexOf(lastLoadedPost);
|
||||
const stream = this.get('stream');
|
||||
const lastIndex = this.indexOf(lastLoadedPost);
|
||||
if (lastIndex === -1) { return []; }
|
||||
if ((lastIndex + 1) >= this.get('highest_post_number')) { return []; }
|
||||
|
||||
|
@ -109,41 +102,26 @@ Discourse.PostStream = Em.Object.extend({
|
|||
return stream.slice(lastIndex+1, lastIndex + this.get('topic.chunk_size') + 1);
|
||||
}.property('lastLoadedPost', 'stream.@each'),
|
||||
|
||||
|
||||
/**
|
||||
Cancel any active filters on the stream.
|
||||
|
||||
@method cancelFilter
|
||||
**/
|
||||
cancelFilter: function() {
|
||||
cancelFilter() {
|
||||
this.set('summary', false);
|
||||
this.set('show_deleted', false);
|
||||
this.get('userFilters').clear();
|
||||
},
|
||||
|
||||
/**
|
||||
Toggle summary mode for the stream.
|
||||
|
||||
@method toggleSummary
|
||||
**/
|
||||
toggleSummary: function() {
|
||||
toggleSummary() {
|
||||
this.get('userFilters').clear();
|
||||
this.toggleProperty('summary');
|
||||
return this.refresh();
|
||||
},
|
||||
|
||||
toggleDeleted: function() {
|
||||
toggleDeleted() {
|
||||
this.toggleProperty('show_deleted');
|
||||
return this.refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
Filter the stream to a particular user.
|
||||
|
||||
@method toggleParticipant
|
||||
**/
|
||||
toggleParticipant: function(username) {
|
||||
var userFilters = this.get('userFilters');
|
||||
// Filter the stream to a particular user.
|
||||
toggleParticipant(username) {
|
||||
const userFilters = this.get('userFilters');
|
||||
this.set('summary', false);
|
||||
this.set('show_deleted', true);
|
||||
if (userFilters.contains(username)) {
|
||||
|
@ -157,22 +135,16 @@ Discourse.PostStream = Em.Object.extend({
|
|||
/**
|
||||
Loads a new set of posts into the stream. If you provide a `nearPost` option and the post
|
||||
is already loaded, it will simply scroll there and load nothing.
|
||||
|
||||
@method refresh
|
||||
@param {Object} opts Options for loading the stream
|
||||
@param {Integer} opts.nearPost The post we want to find other posts near to.
|
||||
@param {Boolean} opts.track_visit Whether or not to track this as a visit to a topic.
|
||||
@returns {Promise} a promise that is resolved when the posts have been inserted into the stream.
|
||||
**/
|
||||
refresh: function(opts) {
|
||||
refresh(opts) {
|
||||
opts = opts || {};
|
||||
opts.nearPost = parseInt(opts.nearPost, 10);
|
||||
|
||||
var topic = this.get('topic'),
|
||||
const topic = this.get('topic'),
|
||||
self = this;
|
||||
|
||||
// Do we already have the post in our list of posts? Jump there.
|
||||
var postWeWant = this.get('posts').findProperty('post_number', opts.nearPost);
|
||||
const postWeWant = this.get('posts').findProperty('post_number', opts.nearPost);
|
||||
if (postWeWant) { return Ember.RSVP.resolve(); }
|
||||
|
||||
// TODO: if we have all the posts in the filter, don't go to the server for them.
|
||||
|
@ -192,10 +164,10 @@ Discourse.PostStream = Em.Object.extend({
|
|||
},
|
||||
hasLoadedData: Em.computed.and('hasPosts', 'hasStream'),
|
||||
|
||||
collapsePosts: function(from, to){
|
||||
var posts = this.get('posts');
|
||||
var remove = posts.filter(function(post){
|
||||
var postNumber = post.get('post_number');
|
||||
collapsePosts(from, to){
|
||||
const posts = this.get('posts');
|
||||
const remove = posts.filter(function(post){
|
||||
const postNumber = post.get('post_number');
|
||||
return postNumber >= from && postNumber <= to;
|
||||
});
|
||||
|
||||
|
@ -203,9 +175,9 @@ Discourse.PostStream = Em.Object.extend({
|
|||
|
||||
// make gap
|
||||
this.set('gaps', this.get('gaps') || {before: {}, after: {}});
|
||||
var before = this.get('gaps.before');
|
||||
const before = this.get('gaps.before');
|
||||
|
||||
var post = posts.find(function(post){
|
||||
const post = posts.find(function(post){
|
||||
return post.get('post_number') > to;
|
||||
});
|
||||
|
||||
|
@ -218,16 +190,9 @@ Discourse.PostStream = Em.Object.extend({
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
Fill in a gap of posts before a particular post
|
||||
|
||||
@method fillGapBefore
|
||||
@paaram {Discourse.Post} post beside gap
|
||||
@paaram {Array} gap array of post ids to load
|
||||
@returns {Promise} a promise that's resolved when the posts have been added.
|
||||
**/
|
||||
fillGapBefore: function(post, gap) {
|
||||
var postId = post.get('id'),
|
||||
// Fill in a gap of posts before a particular post
|
||||
fillGapBefore(post, gap) {
|
||||
const postId = post.get('id'),
|
||||
stream = this.get('stream'),
|
||||
idx = stream.indexOf(postId),
|
||||
currentPosts = this.get('posts'),
|
||||
|
@ -237,11 +202,11 @@ Discourse.PostStream = Em.Object.extend({
|
|||
// Insert the gap at the appropriate place
|
||||
stream.splice.apply(stream, [idx, 0].concat(gap));
|
||||
|
||||
var postIdx = currentPosts.indexOf(post);
|
||||
let postIdx = currentPosts.indexOf(post);
|
||||
if (postIdx !== -1) {
|
||||
return this.findPostsByIds(gap).then(function(posts) {
|
||||
posts.forEach(function(p) {
|
||||
var stored = self.storePost(p);
|
||||
const stored = self.storePost(p);
|
||||
if (!currentPosts.contains(stored)) {
|
||||
currentPosts.insertAt(postIdx++, stored);
|
||||
}
|
||||
|
@ -256,16 +221,9 @@ Discourse.PostStream = Em.Object.extend({
|
|||
return Ember.RSVP.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
Fill in a gap of posts after a particular post
|
||||
|
||||
@method fillGapAfter
|
||||
@paaram {Discourse.Post} post beside gap
|
||||
@paaram {Array} gap array of post ids to load
|
||||
@returns {Promise} a promise that's resolved when the posts have been added.
|
||||
**/
|
||||
fillGapAfter: function(post, gap) {
|
||||
var postId = post.get('id'),
|
||||
// Fill in a gap of posts after a particular post
|
||||
fillGapAfter(post, gap) {
|
||||
const postId = post.get('id'),
|
||||
stream = this.get('stream'),
|
||||
idx = stream.indexOf(postId),
|
||||
self = this;
|
||||
|
@ -279,24 +237,19 @@ Discourse.PostStream = Em.Object.extend({
|
|||
return Ember.RSVP.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
Appends the next window of posts to the stream. Call it when scrolling downwards.
|
||||
|
||||
@method appendMore
|
||||
@returns {Promise} a promise that's resolved when the posts have been added.
|
||||
**/
|
||||
appendMore: function() {
|
||||
var self = this;
|
||||
// Appends the next window of posts to the stream. Call it when scrolling downwards.
|
||||
appendMore() {
|
||||
const self = this;
|
||||
|
||||
// Make sure we can append more posts
|
||||
if (!self.get('canAppendMore')) { return Ember.RSVP.resolve(); }
|
||||
|
||||
var postIds = self.get('nextWindow');
|
||||
const postIds = self.get('nextWindow');
|
||||
if (Ember.isEmpty(postIds)) { return Ember.RSVP.resolve(); }
|
||||
|
||||
self.set('loadingBelow', true);
|
||||
|
||||
var stopLoading = function() {
|
||||
const stopLoading = function() {
|
||||
self.set('loadingBelow', false);
|
||||
};
|
||||
|
||||
|
@ -308,19 +261,14 @@ Discourse.PostStream = Em.Object.extend({
|
|||
}, stopLoading);
|
||||
},
|
||||
|
||||
/**
|
||||
Prepend the previous window of posts to the stream. Call it when scrolling upwards.
|
||||
|
||||
@method prependMore
|
||||
@returns {Promise} a promise that's resolved when the posts have been added.
|
||||
**/
|
||||
prependMore: function() {
|
||||
var postStream = this;
|
||||
// Prepend the previous window of posts to the stream. Call it when scrolling upwards.
|
||||
prependMore() {
|
||||
const postStream = this;
|
||||
|
||||
// Make sure we can append more posts
|
||||
if (!postStream.get('canPrependMore')) { return Ember.RSVP.resolve(); }
|
||||
|
||||
var postIds = postStream.get('previousWindow');
|
||||
const postIds = postStream.get('previousWindow');
|
||||
if (Ember.isEmpty(postIds)) { return Ember.RSVP.resolve(); }
|
||||
|
||||
postStream.set('loadingAbove', true);
|
||||
|
@ -336,18 +284,13 @@ Discourse.PostStream = Em.Object.extend({
|
|||
Stage a post for insertion in the stream. It should be rendered right away under the
|
||||
assumption that the post will succeed. We can then `commitPost` when it succeeds or
|
||||
`undoPost` when it fails.
|
||||
|
||||
@method stagePost
|
||||
@param {Discourse.Post} post the post to stage in the stream
|
||||
@param {Discourse.User} user the user creating the post
|
||||
**/
|
||||
stagePost: function(post, user) {
|
||||
|
||||
stagePost(post, user) {
|
||||
// We can't stage two posts simultaneously
|
||||
if (this.get('stagingPost')) { return false; }
|
||||
this.set('stagingPost', true);
|
||||
|
||||
var topic = this.get('topic');
|
||||
const topic = this.get('topic');
|
||||
topic.setProperties({
|
||||
posts_count: (topic.get('posts_count') || 0) + 1,
|
||||
last_posted_at: new Date(),
|
||||
|
@ -371,13 +314,8 @@ Discourse.PostStream = Em.Object.extend({
|
|||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
Commit the post we staged. Call this after a save succeeds.
|
||||
|
||||
@method commitPost
|
||||
@param {Discourse.Post} the post we saved in the stream.
|
||||
**/
|
||||
commitPost: function(post) {
|
||||
// Commit the post we staged. Call this after a save succeeds.
|
||||
commitPost(post) {
|
||||
if (this.get('loadedAllPosts')) {
|
||||
this.appendPost(post);
|
||||
}
|
||||
|
@ -398,16 +336,13 @@ Discourse.PostStream = Em.Object.extend({
|
|||
/**
|
||||
Undo a post we've staged in the stream. Remove it from being rendered and revert the
|
||||
state we changed.
|
||||
|
||||
@method undoPost
|
||||
@param {Discourse.Post} the post to undo from the stream
|
||||
**/
|
||||
undoPost: function(post) {
|
||||
undoPost(post) {
|
||||
this.get('stream').removeObject(-1);
|
||||
this.posts.removeObject(post);
|
||||
this.get('postIdentityMap').set(-1, null);
|
||||
|
||||
var topic = this.get('topic');
|
||||
const topic = this.get('topic');
|
||||
this.set('stagingPost', false);
|
||||
|
||||
topic.setProperties({
|
||||
|
@ -418,44 +353,24 @@ Discourse.PostStream = Em.Object.extend({
|
|||
// TODO unfudge reply count on parent post
|
||||
},
|
||||
|
||||
/**
|
||||
Prepends a single post to the stream.
|
||||
|
||||
@method prependPost
|
||||
@param {Discourse.Post} post The post we're prepending
|
||||
@returns {Discourse.Post} the post that was inserted.
|
||||
**/
|
||||
prependPost: function(post) {
|
||||
prependPost(post) {
|
||||
this.get('posts').unshiftObject(this.storePost(post));
|
||||
return post;
|
||||
},
|
||||
|
||||
/**
|
||||
Appends a single post into the stream.
|
||||
|
||||
@method appendPost
|
||||
@param {Discourse.Post} post The post we're appending
|
||||
@returns {Discourse.Post} the post that was inserted.
|
||||
**/
|
||||
appendPost: function(post) {
|
||||
var stored = this.storePost(post);
|
||||
appendPost(post) {
|
||||
const stored = this.storePost(post);
|
||||
if (stored) {
|
||||
this.get('posts').addObject(stored);
|
||||
}
|
||||
return post;
|
||||
},
|
||||
|
||||
/**
|
||||
Removes posts from the stream.
|
||||
|
||||
@method removePosts
|
||||
@param {Array} posts the collection of posts to remove
|
||||
**/
|
||||
removePosts: function(posts) {
|
||||
removePosts(posts) {
|
||||
if (Em.isEmpty(posts)) { return; }
|
||||
|
||||
var postIds = posts.map(function (p) { return p.get('id'); });
|
||||
var identityMap = this.get('postIdentityMap');
|
||||
const postIds = posts.map(function (p) { return p.get('id'); });
|
||||
const identityMap = this.get('postIdentityMap');
|
||||
|
||||
this.get('stream').removeObjects(postIds);
|
||||
this.get('posts').removeObjects(posts);
|
||||
|
@ -464,14 +379,8 @@ Discourse.PostStream = Em.Object.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Returns a post from the identity map if it's been inserted.
|
||||
|
||||
@method findLoadedPost
|
||||
@param {Integer} id The post we want from the identity map.
|
||||
@returns {Discourse.Post} the post that was inserted.
|
||||
**/
|
||||
findLoadedPost: function(id) {
|
||||
// Returns a post from the identity map if it's been inserted.
|
||||
findLoadedPost(id) {
|
||||
return this.get('postIdentityMap').get(id);
|
||||
},
|
||||
|
||||
|
@ -479,17 +388,14 @@ Discourse.PostStream = Em.Object.extend({
|
|||
Finds and adds a post to the stream by id. Typically this would happen if we receive a message
|
||||
from the message bus indicating there's a new post. We'll only insert it if we currently
|
||||
have no filters.
|
||||
|
||||
@method triggerNewPostInStream
|
||||
@param {Integer} postId The id of the new post to be inserted into the stream
|
||||
**/
|
||||
triggerNewPostInStream: function(postId) {
|
||||
triggerNewPostInStream(postId) {
|
||||
if (!postId) { return; }
|
||||
|
||||
// We only trigger if there are no filters active
|
||||
if (!this.get('hasNoFilters')) { return; }
|
||||
|
||||
var loadedAllPosts = this.get('loadedAllPosts');
|
||||
const loadedAllPosts = this.get('loadedAllPosts');
|
||||
|
||||
if (this.get('stream').indexOf(postId) === -1) {
|
||||
this.get('stream').addObject(postId);
|
||||
|
@ -497,8 +403,8 @@ Discourse.PostStream = Em.Object.extend({
|
|||
}
|
||||
},
|
||||
|
||||
triggerRecoveredPost: function(postId){
|
||||
var self = this,
|
||||
triggerRecoveredPost(postId){
|
||||
const self = this,
|
||||
postIdentityMap = this.get('postIdentityMap'),
|
||||
existing = postIdentityMap.get(postId);
|
||||
|
||||
|
@ -506,15 +412,15 @@ Discourse.PostStream = Em.Object.extend({
|
|||
this.triggerChangedPost(postId, new Date());
|
||||
} else {
|
||||
// need to insert into stream
|
||||
var url = "/posts/" + postId;
|
||||
const url = "/posts/" + postId;
|
||||
Discourse.ajax(url).then(function(p){
|
||||
var post = Discourse.Post.create(p);
|
||||
var stream = self.get("stream");
|
||||
var posts = self.get("posts");
|
||||
const post = Discourse.Post.create(p);
|
||||
const stream = self.get("stream");
|
||||
const posts = self.get("posts");
|
||||
self.storePost(post);
|
||||
|
||||
// we need to zip this into the stream
|
||||
var index = 0;
|
||||
let index = 0;
|
||||
stream.forEach(function(postId){
|
||||
if(postId < p.id){
|
||||
index+= 1;
|
||||
|
@ -541,13 +447,13 @@ Discourse.PostStream = Em.Object.extend({
|
|||
}
|
||||
},
|
||||
|
||||
triggerDeletedPost: function(postId){
|
||||
var self = this,
|
||||
triggerDeletedPost(postId){
|
||||
const self = this,
|
||||
postIdentityMap = this.get('postIdentityMap'),
|
||||
existing = postIdentityMap.get(postId);
|
||||
|
||||
if(existing){
|
||||
var url = "/posts/" + postId;
|
||||
const url = "/posts/" + postId;
|
||||
Discourse.ajax(url).then(
|
||||
function(p){
|
||||
self.storePost(Discourse.Post.create(p));
|
||||
|
@ -558,30 +464,24 @@ Discourse.PostStream = Em.Object.extend({
|
|||
}
|
||||
},
|
||||
|
||||
triggerChangedPost: function(postId, updatedAt) {
|
||||
triggerChangedPost(postId, updatedAt) {
|
||||
if (!postId) { return; }
|
||||
|
||||
var postIdentityMap = this.get('postIdentityMap'),
|
||||
const postIdentityMap = this.get('postIdentityMap'),
|
||||
existing = postIdentityMap.get(postId),
|
||||
self = this;
|
||||
|
||||
if (existing && existing.updated_at !== updatedAt) {
|
||||
var url = "/posts/" + postId;
|
||||
const url = "/posts/" + postId;
|
||||
Discourse.ajax(url).then(function(p){
|
||||
self.storePost(Discourse.Post.create(p));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
Returns the "thread" of posts in the history of a post.
|
||||
|
||||
@method findReplyHistory
|
||||
@param {Discourse.Post} post the post whose history we want
|
||||
@returns {Array} the posts in the history.
|
||||
**/
|
||||
findReplyHistory: function(post) {
|
||||
var postStream = this,
|
||||
// Returns the "thread" of posts in the history of a post.
|
||||
findReplyHistory(post) {
|
||||
const postStream = this,
|
||||
url = "/posts/" + post.get('id') + "/reply-history.json?max_replies=" + Discourse.SiteSettings.max_reply_history;
|
||||
|
||||
return Discourse.ajax(url).then(function(result) {
|
||||
|
@ -597,16 +497,11 @@ Discourse.PostStream = Em.Object.extend({
|
|||
Returns the closest post given a postNumber that may not exist in the stream.
|
||||
For example, if the user asks for a post that's deleted or otherwise outside the range.
|
||||
This allows us to set the progress bar with the correct number.
|
||||
|
||||
@method closestPostForPostNumber
|
||||
@param {Number} postNumber the post number we're looking for
|
||||
@return {Post} the closest post
|
||||
@see PostStream.closestPostNumberFor
|
||||
**/
|
||||
closestPostForPostNumber: function(postNumber) {
|
||||
closestPostForPostNumber(postNumber) {
|
||||
if (!this.get('hasPosts')) { return; }
|
||||
|
||||
var closest = null;
|
||||
let closest = null;
|
||||
this.get('posts').forEach(function (p) {
|
||||
if (!closest) {
|
||||
closest = p;
|
||||
|
@ -628,17 +523,12 @@ Discourse.PostStream = Em.Object.extend({
|
|||
@returns {Number} 1-starting index of the post, or 0 if not found
|
||||
@see PostStream.progressIndexOfPostId
|
||||
**/
|
||||
progressIndexOfPost: function(post) {
|
||||
progressIndexOfPost(post) {
|
||||
return this.progressIndexOfPostId(post.get('id'));
|
||||
},
|
||||
|
||||
/**
|
||||
Get the index in the stream of a post id. (Use this for the topic progress bar.)
|
||||
|
||||
@param post_id - post id to search for
|
||||
@returns {Number} 1-starting index of the post, or 0 if not found
|
||||
**/
|
||||
progressIndexOfPostId: function(post_id) {
|
||||
// Get the index in the stream of a post id. (Use this for the topic progress bar.)
|
||||
progressIndexOfPostId(post_id) {
|
||||
return this.get('stream').indexOf(post_id) + 1;
|
||||
},
|
||||
|
||||
|
@ -646,15 +536,11 @@ Discourse.PostStream = Em.Object.extend({
|
|||
Returns the closest post number given a postNumber that may not exist in the stream.
|
||||
For example, if the user asks for a post that's deleted or otherwise outside the range.
|
||||
This allows us to set the progress bar with the correct number.
|
||||
|
||||
@method closestPostNumberFor
|
||||
@param {Number} postNumber the post number we're looking for
|
||||
@return {Number} a close post number
|
||||
**/
|
||||
closestPostNumberFor: function(postNumber) {
|
||||
closestPostNumberFor(postNumber) {
|
||||
if (!this.get('hasPosts')) { return; }
|
||||
|
||||
var closest = null;
|
||||
let closest = null;
|
||||
this.get('posts').forEach(function (p) {
|
||||
if (closest === postNumber) { return; }
|
||||
if (!closest) { closest = p.get('post_number'); }
|
||||
|
@ -668,41 +554,33 @@ Discourse.PostStream = Em.Object.extend({
|
|||
},
|
||||
|
||||
// Find a postId for a postNumber, respecting gaps
|
||||
findPostIdForPostNumber: function(postNumber) {
|
||||
var count = 1,
|
||||
stream = this.get('stream'),
|
||||
beforeLookup = this.get('gaps.before'),
|
||||
streamLength = stream.length;
|
||||
findPostIdForPostNumber(postNumber) {
|
||||
const stream = this.get('stream'),
|
||||
beforeLookup = this.get('gaps.before'),
|
||||
streamLength = stream.length;
|
||||
|
||||
for (var i=0; i<streamLength; i++) {
|
||||
var pid = stream[i];
|
||||
let sum = 1;
|
||||
for (let i=0; i<streamLength; i++) {
|
||||
const pid = stream[i];
|
||||
|
||||
// See if there are posts before this post
|
||||
if (beforeLookup) {
|
||||
var before = beforeLookup[pid];
|
||||
const before = beforeLookup[pid];
|
||||
if (before) {
|
||||
for (var j=0; j<before.length; j++) {
|
||||
if (count === postNumber) { return pid; }
|
||||
count++;
|
||||
for (let j=0; j<before.length; j++) {
|
||||
if (sum === postNumber) { return pid; }
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count === postNumber) { return pid; }
|
||||
count++;
|
||||
if (sum === postNumber) { return pid; }
|
||||
sum++;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Given a JSON packet, update this stream and the posts that exist in it.
|
||||
|
||||
@param {Object} postStreamData The JSON data we want to update from.
|
||||
@method updateFromJson
|
||||
**/
|
||||
updateFromJson: function(postStreamData) {
|
||||
var postStream = this,
|
||||
updateFromJson(postStreamData) {
|
||||
const postStream = this,
|
||||
posts = this.get('posts');
|
||||
|
||||
posts.clear();
|
||||
|
@ -720,23 +598,17 @@ Discourse.PostStream = Em.Object.extend({
|
|||
},
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Stores a post in our identity map, and sets up the references it needs to
|
||||
find associated objects like the topic. It might return a different reference
|
||||
than you supplied if the post has already been loaded.
|
||||
|
||||
@method storePost
|
||||
@param {Discourse.Post} post The post we're storing in the identity map
|
||||
@returns {Discourse.Post} the post from the identity map
|
||||
**/
|
||||
storePost: function(post) {
|
||||
storePost(post) {
|
||||
// Calling `Em.get(undefined` raises an error
|
||||
if (!post) { return; }
|
||||
|
||||
var postId = Em.get(post, 'id');
|
||||
const postId = Em.get(post, 'id');
|
||||
if (postId) {
|
||||
var postIdentityMap = this.get('postIdentityMap'),
|
||||
const postIdentityMap = this.get('postIdentityMap'),
|
||||
existing = postIdentityMap.get(post.get('id'));
|
||||
|
||||
if (existing) {
|
||||
|
@ -752,7 +624,7 @@ Discourse.PostStream = Em.Object.extend({
|
|||
postIdentityMap.set(post.get('id'), post);
|
||||
|
||||
// Update the `highest_post_number` if this post is higher.
|
||||
var postNumber = post.get('post_number');
|
||||
const postNumber = post.get('post_number');
|
||||
if (postNumber && postNumber > (this.get('topic.highest_post_number') || 0)) {
|
||||
this.set('topic.highest_post_number', postNumber);
|
||||
}
|
||||
|
@ -761,17 +633,11 @@ Discourse.PostStream = Em.Object.extend({
|
|||
},
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Given a list of postIds, returns a list of the posts we don't have in our
|
||||
identity map and need to load.
|
||||
|
||||
@method listUnloadedIds
|
||||
@param {Array} postIds The post Ids we want to load from the server
|
||||
@returns {Array} the array of postIds we don't have loaded.
|
||||
**/
|
||||
listUnloadedIds: function(postIds) {
|
||||
var unloaded = Em.A(),
|
||||
listUnloadedIds(postIds) {
|
||||
const unloaded = Em.A(),
|
||||
postIdentityMap = this.get('postIdentityMap');
|
||||
postIds.forEach(function(p) {
|
||||
if (!postIdentityMap.has(p)) { unloaded.pushObject(p); }
|
||||
|
@ -779,17 +645,8 @@ Discourse.PostStream = Em.Object.extend({
|
|||
return unloaded;
|
||||
},
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Returns a list of posts in order requested, by id.
|
||||
|
||||
@method findPostsByIds
|
||||
@param {Array} postIds The post Ids we want to retrieve, in order.
|
||||
@returns {Promise} a promise that will resolve to the posts in the order requested.
|
||||
**/
|
||||
findPostsByIds: function(postIds) {
|
||||
var unloaded = this.listUnloadedIds(postIds),
|
||||
findPostsByIds(postIds) {
|
||||
const unloaded = this.listUnloadedIds(postIds),
|
||||
postIdentityMap = this.get('postIdentityMap');
|
||||
|
||||
// Load our unloaded posts by id
|
||||
|
@ -800,27 +657,18 @@ Discourse.PostStream = Em.Object.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Loads a list of posts from the server and inserts them into our identity map.
|
||||
|
||||
@method loadIntoIdentityMap
|
||||
@param {Array} postIds The post Ids we want to insert into the identity map.
|
||||
@returns {Promise} a promise that will resolve to the posts in the order requested.
|
||||
**/
|
||||
loadIntoIdentityMap: function(postIds) {
|
||||
loadIntoIdentityMap(postIds) {
|
||||
// If we don't want any posts, return a promise that resolves right away
|
||||
if (Em.isEmpty(postIds)) {
|
||||
return Ember.RSVP.resolve();
|
||||
}
|
||||
|
||||
var url = "/t/" + this.get('topic.id') + "/posts.json",
|
||||
const url = "/t/" + this.get('topic.id') + "/posts.json",
|
||||
data = { post_ids: postIds },
|
||||
postStream = this;
|
||||
|
||||
return Discourse.ajax(url, {data: data}).then(function(result) {
|
||||
var posts = Em.get(result, "post_stream.posts");
|
||||
const posts = Em.get(result, "post_stream.posts");
|
||||
if (posts) {
|
||||
posts.forEach(function (p) {
|
||||
postStream.storePost(Discourse.Post.create(p));
|
||||
|
@ -830,33 +678,19 @@ Discourse.PostStream = Em.Object.extend({
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Returns the index of a particular post in the stream
|
||||
|
||||
@method indexOf
|
||||
@param {Discourse.Post} post The post we're looking for
|
||||
**/
|
||||
indexOf: function(post) {
|
||||
indexOf(post) {
|
||||
return this.get('stream').indexOf(post.get('id'));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
@private
|
||||
|
||||
Handles an error loading a topic based on a HTTP status code. Updates
|
||||
the text to the correct values.
|
||||
|
||||
@method errorLoading
|
||||
@param {Integer} status the HTTP status code
|
||||
@param {Discourse.Topic} topic The topic instance we were trying to load
|
||||
**/
|
||||
errorLoading: function(result) {
|
||||
var status = result.status;
|
||||
errorLoading(result) {
|
||||
const status = result.status;
|
||||
|
||||
var topic = this.get('topic');
|
||||
const topic = this.get('topic');
|
||||
topic.set('loadingFilter', false);
|
||||
topic.set('errorLoading', true);
|
||||
|
||||
|
@ -887,10 +721,10 @@ Discourse.PostStream = Em.Object.extend({
|
|||
});
|
||||
|
||||
|
||||
Discourse.PostStream.reopenClass({
|
||||
PostStream.reopenClass({
|
||||
|
||||
create: function() {
|
||||
var postStream = this._super.apply(this, arguments);
|
||||
create() {
|
||||
const postStream = this._super.apply(this, arguments);
|
||||
postStream.setProperties({
|
||||
posts: [],
|
||||
stream: [],
|
||||
|
@ -906,9 +740,9 @@ Discourse.PostStream.reopenClass({
|
|||
return postStream;
|
||||
},
|
||||
|
||||
loadTopicView: function(topicId, args) {
|
||||
var opts = _.merge({}, args),
|
||||
url = Discourse.getURL("/t/") + topicId;
|
||||
loadTopicView(topicId, args) {
|
||||
const opts = _.merge({}, args);
|
||||
let url = Discourse.getURL("/t/") + topicId;
|
||||
if (opts.nearPost) {
|
||||
url += "/" + opts.nearPost;
|
||||
}
|
||||
|
@ -921,3 +755,5 @@ Discourse.PostStream.reopenClass({
|
|||
}
|
||||
|
||||
});
|
||||
|
||||
export default PostStream;
|
|
@ -1,16 +1,13 @@
|
|||
/**
|
||||
A model representing a Topic's details that aren't always present, such as a list of participants.
|
||||
When showing topics in lists and such this information should not be required.
|
||||
|
||||
@class TopicDetails
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.TopicDetails = Discourse.Model.extend({
|
||||
const TopicDetails = Discourse.Model.extend({
|
||||
loaded: false,
|
||||
|
||||
updateFromJson: function(details) {
|
||||
updateFromJson(details) {
|
||||
const topic = this.get('topic');
|
||||
|
||||
if (details.allowed_users) {
|
||||
details.allowed_users = details.allowed_users.map(function (u) {
|
||||
return Discourse.User.create(u);
|
||||
|
@ -24,10 +21,9 @@ Discourse.TopicDetails = Discourse.Model.extend({
|
|||
}
|
||||
|
||||
if (details.participants) {
|
||||
var topic = this.get('topic');
|
||||
details.participants = details.participants.map(function (p) {
|
||||
p.topic = topic;
|
||||
return Em.Object.create(p);
|
||||
return Ember.Object.create(p);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -59,7 +55,7 @@ Discourse.TopicDetails = Discourse.Model.extend({
|
|||
}.property('notification_level', 'notifications_reason_id'),
|
||||
|
||||
|
||||
updateNotifications: function(v) {
|
||||
updateNotifications(v) {
|
||||
this.set('notification_level', v);
|
||||
this.set('notifications_reason_id', null);
|
||||
return Discourse.ajax("/t/" + (this.get('topic.id')) + "/notifications", {
|
||||
|
@ -68,7 +64,7 @@ Discourse.TopicDetails = Discourse.Model.extend({
|
|||
});
|
||||
},
|
||||
|
||||
removeAllowedUser: function(user) {
|
||||
removeAllowedUser(user) {
|
||||
var users = this.get('allowed_users'),
|
||||
username = user.get('username');
|
||||
|
||||
|
@ -80,3 +76,5 @@ Discourse.TopicDetails = Discourse.Model.extend({
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default TopicDetails;
|
|
@ -1,8 +1,11 @@
|
|||
Discourse.Topic = Discourse.Model.extend({
|
||||
import TopicDetails from 'discourse/models/topic-details';
|
||||
import PostStream from 'discourse/models/post-stream';
|
||||
|
||||
const Topic = Discourse.Model.extend({
|
||||
|
||||
// returns createdAt if there's no bumped date
|
||||
bumpedAt: function() {
|
||||
var bumpedAt = this.get('bumped_at');
|
||||
const bumpedAt = this.get('bumped_at');
|
||||
if (bumpedAt) {
|
||||
return new Date(bumpedAt);
|
||||
} else {
|
||||
|
@ -20,11 +23,11 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property('created_at'),
|
||||
|
||||
postStream: function() {
|
||||
return Discourse.PostStream.create({topic: this});
|
||||
return PostStream.create({topic: this});
|
||||
}.property(),
|
||||
|
||||
details: function() {
|
||||
return Discourse.TopicDetails.create({topic: this});
|
||||
return TopicDetails.create({topic: this});
|
||||
}.property(),
|
||||
|
||||
invisible: Em.computed.not('visible'),
|
||||
|
@ -35,12 +38,12 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property('id'),
|
||||
|
||||
category: function() {
|
||||
var categoryId = this.get('category_id');
|
||||
const categoryId = this.get('category_id');
|
||||
if (categoryId) {
|
||||
return Discourse.Category.list().findProperty('id', categoryId);
|
||||
}
|
||||
|
||||
var categoryName = this.get('categoryName');
|
||||
const categoryName = this.get('categoryName');
|
||||
if (categoryName) {
|
||||
return Discourse.Category.list().findProperty('name', categoryName);
|
||||
}
|
||||
|
@ -52,12 +55,12 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property('category.fullSlug'),
|
||||
|
||||
shareUrl: function(){
|
||||
var user = Discourse.User.current();
|
||||
const user = Discourse.User.current();
|
||||
return this.get('url') + (user ? '?u=' + user.get('username_lower') : '');
|
||||
}.property('url'),
|
||||
|
||||
url: function() {
|
||||
var slug = this.get('slug');
|
||||
let slug = this.get('slug');
|
||||
if (slug.trim().length === 0) {
|
||||
slug = "topic";
|
||||
}
|
||||
|
@ -65,8 +68,8 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property('id', 'slug'),
|
||||
|
||||
// Helper to build a Url with a post number
|
||||
urlForPostNumber: function(postNumber) {
|
||||
var url = this.get('url');
|
||||
urlForPostNumber(postNumber) {
|
||||
let url = this.get('url');
|
||||
if (postNumber && (postNumber > 0)) {
|
||||
url += "/" + postNumber;
|
||||
}
|
||||
|
@ -74,7 +77,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
},
|
||||
|
||||
totalUnread: function() {
|
||||
var count = (this.get('unread') || 0) + (this.get('new_posts') || 0);
|
||||
const count = (this.get('unread') || 0) + (this.get('new_posts') || 0);
|
||||
return count > 0 ? count : null;
|
||||
}.property('new_posts', 'unread'),
|
||||
|
||||
|
@ -83,7 +86,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property('url', 'last_read_post_number'),
|
||||
|
||||
lastUnreadUrl: function() {
|
||||
var postNumber = Math.min(this.get('last_read_post_number') + 1, this.get('highest_post_number'));
|
||||
const postNumber = Math.min(this.get('last_read_post_number') + 1, this.get('highest_post_number'));
|
||||
return this.urlForPostNumber(postNumber);
|
||||
}.property('url', 'last_read_post_number', 'highest_post_number'),
|
||||
|
||||
|
@ -107,12 +110,11 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
// tells us if we are still asynchronously flushing our "recently read" data.
|
||||
// So take what the browser has seen into consideration.
|
||||
displayNewPosts: function() {
|
||||
var delta, result;
|
||||
var highestSeen = Discourse.Session.currentProp('highestSeenByTopic')[this.get('id')];
|
||||
const highestSeen = Discourse.Session.currentProp('highestSeenByTopic')[this.get('id')];
|
||||
if (highestSeen) {
|
||||
delta = highestSeen - this.get('last_read_post_number');
|
||||
let delta = highestSeen - this.get('last_read_post_number');
|
||||
if (delta > 0) {
|
||||
result = this.get('new_posts') - delta;
|
||||
let result = this.get('new_posts') - delta;
|
||||
if (result < 0) {
|
||||
result = 0;
|
||||
}
|
||||
|
@ -123,7 +125,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property('new_posts', 'id'),
|
||||
|
||||
viewsHeat: function() {
|
||||
var v = this.get('views');
|
||||
const v = this.get('views');
|
||||
if( v >= Discourse.SiteSettings.topic_views_heat_high ) return 'heatmap-high';
|
||||
if( v >= Discourse.SiteSettings.topic_views_heat_medium ) return 'heatmap-med';
|
||||
if( v >= Discourse.SiteSettings.topic_views_heat_low ) return 'heatmap-low';
|
||||
|
@ -137,17 +139,17 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
isPrivateMessage: Em.computed.equal('archetype', 'private_message'),
|
||||
isBanner: Em.computed.equal('archetype', 'banner'),
|
||||
|
||||
toggleStatus: function(property) {
|
||||
toggleStatus(property) {
|
||||
this.toggleProperty(property);
|
||||
this.saveStatus(property, this.get(property) ? true : false);
|
||||
},
|
||||
|
||||
setStatus: function(property, value) {
|
||||
setStatus(property, value) {
|
||||
this.set(property, value);
|
||||
this.saveStatus(property, value);
|
||||
},
|
||||
|
||||
saveStatus: function(property, value) {
|
||||
saveStatus(property, value) {
|
||||
if (property === 'closed' && value === true) {
|
||||
this.set('details.auto_close_at', null);
|
||||
}
|
||||
|
@ -160,28 +162,28 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
});
|
||||
},
|
||||
|
||||
makeBanner: function() {
|
||||
var self = this;
|
||||
makeBanner() {
|
||||
const self = this;
|
||||
return Discourse.ajax('/t/' + this.get('id') + '/make-banner', { type: 'PUT' })
|
||||
.then(function () { self.set('archetype', 'banner'); });
|
||||
},
|
||||
|
||||
removeBanner: function() {
|
||||
var self = this;
|
||||
removeBanner() {
|
||||
const self = this;
|
||||
return Discourse.ajax('/t/' + this.get('id') + '/remove-banner', { type: 'PUT' })
|
||||
.then(function () { self.set('archetype', 'regular'); });
|
||||
},
|
||||
|
||||
estimatedReadingTime: function() {
|
||||
var wordCount = this.get('word_count');
|
||||
const wordCount = this.get('word_count');
|
||||
if (!wordCount) return;
|
||||
|
||||
// Avg for 500 words per minute when you account for skimming
|
||||
return Math.floor(wordCount / 500.0);
|
||||
}.property('word_count'),
|
||||
|
||||
toggleBookmark: function() {
|
||||
var self = this, firstPost = this.get("postStream.posts")[0];
|
||||
toggleBookmark() {
|
||||
const self = this, firstPost = this.get("postStream.posts")[0];
|
||||
|
||||
this.toggleProperty('bookmarked');
|
||||
if (this.get("postStream.firstPostPresent")) { firstPost.toggleProperty("bookmarked"); }
|
||||
|
@ -194,8 +196,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
self.toggleProperty('bookmarked');
|
||||
if (self.get("postStream.firstPostPresent")) { firstPost.toggleProperty('bookmarked'); }
|
||||
|
||||
var showGenericError = true;
|
||||
|
||||
let showGenericError = true;
|
||||
if (error && error.responseText) {
|
||||
try {
|
||||
bootbox.alert($.parseJSON(error.responseText).errors);
|
||||
|
@ -209,13 +210,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Invite a user to this topic
|
||||
|
||||
@method createInvite
|
||||
@param {String} emailOrUsername The email or username of the user to be invited
|
||||
**/
|
||||
createInvite: function(emailOrUsername, groupNames) {
|
||||
createInvite(emailOrUsername, groupNames) {
|
||||
return Discourse.ajax("/t/" + this.get('id') + "/invite", {
|
||||
type: 'POST',
|
||||
data: { user: emailOrUsername, group_names: groupNames }
|
||||
|
@ -223,7 +218,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
},
|
||||
|
||||
// Delete this topic
|
||||
destroy: function(deleted_by) {
|
||||
destroy(deleted_by) {
|
||||
this.setProperties({
|
||||
deleted_at: new Date(),
|
||||
deleted_by: deleted_by,
|
||||
|
@ -237,7 +232,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
},
|
||||
|
||||
// Recover this topic if deleted
|
||||
recover: function() {
|
||||
recover() {
|
||||
this.setProperties({
|
||||
deleted_at: null,
|
||||
deleted_by: null,
|
||||
|
@ -248,14 +243,14 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
},
|
||||
|
||||
// Update our attributes from a JSON result
|
||||
updateFromJson: function(json) {
|
||||
updateFromJson(json) {
|
||||
this.get('details').updateFromJson(json.details);
|
||||
|
||||
var keys = Object.keys(json);
|
||||
const keys = Object.keys(json);
|
||||
keys.removeObject('details');
|
||||
keys.removeObject('post_stream');
|
||||
|
||||
var topic = this;
|
||||
const topic = this;
|
||||
keys.forEach(function (key) {
|
||||
topic.set(key, json[key]);
|
||||
});
|
||||
|
@ -266,13 +261,8 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
return this.get('pinned') && this.get('category.isUncategorizedCategory');
|
||||
}.property('pinned', 'category.isUncategorizedCategory'),
|
||||
|
||||
/**
|
||||
Clears the pin from a topic for the currently logged in user
|
||||
|
||||
@method clearPin
|
||||
**/
|
||||
clearPin: function() {
|
||||
var topic = this;
|
||||
clearPin() {
|
||||
const topic = this;
|
||||
|
||||
// Clear the pin optimistically from the object
|
||||
topic.set('pinned', false);
|
||||
|
@ -287,7 +277,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
});
|
||||
},
|
||||
|
||||
togglePinnedForUser: function() {
|
||||
togglePinnedForUser() {
|
||||
if (this.get('pinned')) {
|
||||
this.clearPin();
|
||||
} else {
|
||||
|
@ -295,13 +285,8 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
Re-pins a topic with a cleared pin
|
||||
|
||||
@method rePin
|
||||
**/
|
||||
rePin: function() {
|
||||
var topic = this;
|
||||
rePin() {
|
||||
const topic = this;
|
||||
|
||||
// Clear the pin optimistically from the object
|
||||
topic.set('pinned', true);
|
||||
|
@ -317,12 +302,12 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
},
|
||||
|
||||
// Is the reply to a post directly below it?
|
||||
isReplyDirectlyBelow: function(post) {
|
||||
var posts = this.get('postStream.posts');
|
||||
var postNumber = post.get('post_number');
|
||||
isReplyDirectlyBelow(post) {
|
||||
const posts = this.get('postStream.posts');
|
||||
const postNumber = post.get('post_number');
|
||||
if (!posts) return;
|
||||
|
||||
var postBelow = posts[posts.indexOf(post) + 1];
|
||||
const postBelow = posts[posts.indexOf(post) + 1];
|
||||
|
||||
// If the post directly below's reply_to_post_number is our post number or we are quoted,
|
||||
// it's considered directly below.
|
||||
|
@ -340,7 +325,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
hasExcerpt: Em.computed.and('pinned', 'excerptNotEmpty'),
|
||||
|
||||
excerptTruncated: function() {
|
||||
var e = this.get('excerpt');
|
||||
const e = this.get('excerpt');
|
||||
return( e && e.substr(e.length - 8,8) === '…' );
|
||||
}.property('excerpt'),
|
||||
|
||||
|
@ -349,7 +334,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
|
||||
});
|
||||
|
||||
Discourse.Topic.reopenClass({
|
||||
Topic.reopenClass({
|
||||
NotificationLevel: {
|
||||
WATCHING: 3,
|
||||
TRACKING: 2,
|
||||
|
@ -357,13 +342,13 @@ Discourse.Topic.reopenClass({
|
|||
MUTED: 0
|
||||
},
|
||||
|
||||
createActionSummary: function(result) {
|
||||
createActionSummary(result) {
|
||||
if (result.actions_summary) {
|
||||
var lookup = Em.Object.create();
|
||||
const lookup = Em.Object.create();
|
||||
result.actions_summary = result.actions_summary.map(function(a) {
|
||||
a.post = result;
|
||||
a.actionType = Discourse.Site.current().postActionTypeById(a.id);
|
||||
var actionSummary = Discourse.ActionSummary.create(a);
|
||||
const actionSummary = Discourse.ActionSummary.create(a);
|
||||
lookup.set(a.actionType.get('name_key'), actionSummary);
|
||||
return actionSummary;
|
||||
});
|
||||
|
@ -371,7 +356,7 @@ Discourse.Topic.reopenClass({
|
|||
}
|
||||
},
|
||||
|
||||
update: function(topic, props) {
|
||||
update(topic, props) {
|
||||
props = JSON.parse(JSON.stringify(props)) || {};
|
||||
|
||||
// We support `category_id` and `categoryId` for compatibility
|
||||
|
@ -384,7 +369,7 @@ Discourse.Topic.reopenClass({
|
|||
// allows us to make a distinction between arrays that were not
|
||||
// sent and arrays that we specifically want to be empty.
|
||||
Object.keys(props).forEach(function(k) {
|
||||
var v = props[k];
|
||||
const v = props[k];
|
||||
if (v instanceof Array && v.length === 0) {
|
||||
props[k + '_empty_array'] = true;
|
||||
}
|
||||
|
@ -400,24 +385,16 @@ Discourse.Topic.reopenClass({
|
|||
});
|
||||
},
|
||||
|
||||
create: function() {
|
||||
var result = this._super.apply(this, arguments);
|
||||
create() {
|
||||
const result = this._super.apply(this, arguments);
|
||||
this.createActionSummary(result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
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) {
|
||||
findSimilarTo(title, body) {
|
||||
return Discourse.ajax("/topics/similar_to", { data: {title: title, raw: body} }).then(function (results) {
|
||||
if (Array.isArray(results)) {
|
||||
return results.map(function(topic) { return Discourse.Topic.create(topic); });
|
||||
return results.map(function(topic) { return Topic.create(topic); });
|
||||
} else {
|
||||
return Ember.A();
|
||||
}
|
||||
|
@ -425,14 +402,13 @@ Discourse.Topic.reopenClass({
|
|||
},
|
||||
|
||||
// Load a topic, but accepts a set of filters
|
||||
find: function(topicId, opts) {
|
||||
var url = Discourse.getURL("/t/") + topicId;
|
||||
|
||||
find(topicId, opts) {
|
||||
let url = Discourse.getURL("/t/") + topicId;
|
||||
if (opts.nearPost) {
|
||||
url += "/" + opts.nearPost;
|
||||
}
|
||||
|
||||
var data = {};
|
||||
const data = {};
|
||||
if (opts.postsAfter) {
|
||||
data.posts_after = opts.postsAfter;
|
||||
}
|
||||
|
@ -461,8 +437,8 @@ Discourse.Topic.reopenClass({
|
|||
return Discourse.ajax(url + ".json", {data: data});
|
||||
},
|
||||
|
||||
mergeTopic: function(topicId, destinationTopicId) {
|
||||
var promise = Discourse.ajax("/t/" + topicId + "/merge-topic", {
|
||||
mergeTopic(topicId, destinationTopicId) {
|
||||
const promise = Discourse.ajax("/t/" + topicId + "/merge-topic", {
|
||||
type: 'POST',
|
||||
data: {destination_topic_id: destinationTopicId}
|
||||
}).then(function (result) {
|
||||
|
@ -472,8 +448,8 @@ Discourse.Topic.reopenClass({
|
|||
return promise;
|
||||
},
|
||||
|
||||
movePosts: function(topicId, opts) {
|
||||
var promise = Discourse.ajax("/t/" + topicId + "/move-posts", {
|
||||
movePosts(topicId, opts) {
|
||||
const promise = Discourse.ajax("/t/" + topicId + "/move-posts", {
|
||||
type: 'POST',
|
||||
data: opts
|
||||
}).then(function (result) {
|
||||
|
@ -483,8 +459,8 @@ Discourse.Topic.reopenClass({
|
|||
return promise;
|
||||
},
|
||||
|
||||
changeOwners: function(topicId, opts) {
|
||||
var promise = Discourse.ajax("/t/" + topicId + "/change-owner", {
|
||||
changeOwners(topicId, opts) {
|
||||
const promise = Discourse.ajax("/t/" + topicId + "/change-owner", {
|
||||
type: 'POST',
|
||||
data: opts
|
||||
}).then(function (result) {
|
||||
|
@ -494,7 +470,7 @@ Discourse.Topic.reopenClass({
|
|||
return promise;
|
||||
},
|
||||
|
||||
bulkOperation: function(topics, operation) {
|
||||
bulkOperation(topics, operation) {
|
||||
return Discourse.ajax("/topics/bulk", {
|
||||
type: 'PUT',
|
||||
data: {
|
||||
|
@ -504,8 +480,8 @@ Discourse.Topic.reopenClass({
|
|||
});
|
||||
},
|
||||
|
||||
bulkOperationByFilter: function(filter, operation, categoryId) {
|
||||
var data = { filter: filter, operation: operation };
|
||||
bulkOperationByFilter(filter, operation, categoryId) {
|
||||
const data = { filter: filter, operation: operation };
|
||||
if (categoryId) data['category_id'] = categoryId;
|
||||
return Discourse.ajax("/topics/bulk", {
|
||||
type: 'PUT',
|
||||
|
@ -513,12 +489,13 @@ Discourse.Topic.reopenClass({
|
|||
});
|
||||
},
|
||||
|
||||
resetNew: function() {
|
||||
resetNew() {
|
||||
return Discourse.ajax("/topics/reset-new", {type: 'PUT'});
|
||||
},
|
||||
|
||||
idForSlug: function(slug) {
|
||||
idForSlug(slug) {
|
||||
return Discourse.ajax("/t/id_for/" + slug);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default Topic;
|
|
@ -1,6 +1,8 @@
|
|||
import Topic from 'discourse/models/topic';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
model: function(params) {
|
||||
return Discourse.Topic.idForSlug(params.slug);
|
||||
return Topic.idForSlug(params.slug);
|
||||
},
|
||||
|
||||
afterModel: function(result) {
|
||||
|
|
|
@ -4,6 +4,7 @@ var isTransitioning = false,
|
|||
SCROLL_DELAY = 500;
|
||||
|
||||
import ShowFooter from "discourse/mixins/show-footer";
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
var TopicRoute = Discourse.Route.extend(ShowFooter, {
|
||||
redirect: function() { return this.redirectIfLoginRequired(); },
|
||||
|
@ -163,7 +164,7 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
|
|||
return topic;
|
||||
});
|
||||
} else {
|
||||
return this.setupParams(Discourse.Topic.create(_.omit(params, 'username_filters', 'filter')), queryParams);
|
||||
return this.setupParams(Topic.create(_.omit(params, 'username_filters', 'filter')), queryParams);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -154,8 +154,6 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
|
|||
var $wmdPreview = $('#wmd-preview');
|
||||
if ($wmdPreview.length === 0) return;
|
||||
|
||||
Discourse.SyntaxHighlighting.apply($wmdPreview);
|
||||
|
||||
var post = this.get('model.post'),
|
||||
refresh = false;
|
||||
|
||||
|
|
|
@ -264,8 +264,6 @@ var PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
|||
this._showLinkCounts();
|
||||
|
||||
Discourse.ScreenTrack.current().track(this.$().prop('id'), postNumber);
|
||||
Discourse.SyntaxHighlighting.apply($post);
|
||||
Discourse.Lightbox.apply($post);
|
||||
|
||||
this.trigger('postViewInserted', $post);
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
//= require ./discourse/models/model
|
||||
//= require ./discourse/models/user_action
|
||||
//= require ./discourse/models/composer
|
||||
//= require ./discourse/models/post-stream
|
||||
//= require ./discourse/models/topic-details
|
||||
//= require ./discourse/models/topic
|
||||
//= require ./discourse/models/top-period
|
||||
//= require ./discourse/controllers/controller
|
||||
|
|
|
@ -3,8 +3,10 @@ moduleFor('controller:topic', 'controller:topic', {
|
|||
'controller:search', 'controller:topic-progress', 'controller:application']
|
||||
});
|
||||
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
var buildTopic = function() {
|
||||
return Discourse.Topic.create({
|
||||
return Topic.create({
|
||||
title: "Qunit Test Topic",
|
||||
participants: [
|
||||
{id: 1234,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module("Discourse.SelectedPostsCount");
|
||||
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
var buildTestObj = function(params) {
|
||||
return Ember.Object.createWithMixins(Discourse.SelectedPostsCount, params || {});
|
||||
};
|
||||
|
@ -26,7 +28,7 @@ test("when all posts are selected and there is a posts_count", function() {
|
|||
test("when all posts are selected and there is topic with a posts_count", function() {
|
||||
var testObj = buildTestObj({
|
||||
allPostsSelected: true,
|
||||
topic: Discourse.Topic.create({ posts_count: 3456 })
|
||||
topic: Topic.create({ posts_count: 3456 })
|
||||
});
|
||||
|
||||
equal(testObj.get('selectedPostsCount'), 3456, "It returns the topic's posts_count");
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
module("Discourse.PostStream");
|
||||
module("model:post-stream");
|
||||
|
||||
import PostStream from 'discourse/models/post-stream';
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
var buildStream = function(id, stream) {
|
||||
var topic = Discourse.Topic.create({id: id, chunk_size: 5});
|
||||
var topic = Topic.create({id: id, chunk_size: 5});
|
||||
var ps = topic.get('postStream');
|
||||
if (stream) {
|
||||
ps.set('stream', stream);
|
||||
|
@ -12,7 +15,7 @@ var buildStream = function(id, stream) {
|
|||
var participant = {username: 'eviltrout'};
|
||||
|
||||
test('create', function() {
|
||||
ok(Discourse.PostStream.create(), 'it can be created with no parameters');
|
||||
ok(PostStream.create(), 'it can be created with no parameters');
|
||||
});
|
||||
|
||||
test('defaults', function() {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
module("Discourse.TopicDetails");
|
||||
module("model:topic-details");
|
||||
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
var buildDetails = function(id) {
|
||||
var topic = Discourse.Topic.create({id: id});
|
||||
var topic = Topic.create({id: id});
|
||||
return topic.get('details');
|
||||
};
|
||||
|
||||
|
@ -20,13 +22,9 @@ test('updateFromJson', function() {
|
|||
});
|
||||
|
||||
equal(details.get('suggested_topics.length'), 2, 'it loaded the suggested_topics');
|
||||
containsInstance(details.get('suggested_topics'), Discourse.Topic);
|
||||
containsInstance(details.get('suggested_topics'), Topic);
|
||||
|
||||
equal(details.get('allowed_users.length'), 1, 'it loaded the allowed users');
|
||||
containsInstance(details.get('allowed_users'), Discourse.User);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
module("Discourse.Topic");
|
||||
module("model:topic");
|
||||
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
||||
test("defaults", function() {
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
var topic = Topic.create({id: 1234});
|
||||
blank(topic.get('deleted_at'), 'deleted_at defaults to blank');
|
||||
blank(topic.get('deleted_by'), 'deleted_by defaults to blank');
|
||||
});
|
||||
|
||||
test('has details', function() {
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
var topic = Topic.create({id: 1234});
|
||||
var topicDetails = topic.get('details');
|
||||
present(topicDetails, "a topic has topicDetails after we create it");
|
||||
equal(topicDetails.get('topic'), topic, "the topicDetails has a reference back to the topic");
|
||||
});
|
||||
|
||||
test('has a postStream', function() {
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
var topic = Topic.create({id: 1234});
|
||||
var postStream = topic.get('postStream');
|
||||
present(postStream, "a topic has a postStream after we create it");
|
||||
equal(postStream.get('topic'), topic, "the postStream has a reference back to the topic");
|
||||
|
@ -24,13 +26,13 @@ test('has a postStream', function() {
|
|||
test('category relationship', function() {
|
||||
// It finds the category by id
|
||||
var category = Discourse.Category.list()[0],
|
||||
topic = Discourse.Topic.create({id: 1111, category_id: category.get('id') });
|
||||
topic = Topic.create({id: 1111, category_id: category.get('id') });
|
||||
|
||||
equal(topic.get('category'), category);
|
||||
});
|
||||
|
||||
test("updateFromJson", function() {
|
||||
var topic = Discourse.Topic.create({id: 1234}),
|
||||
var topic = Topic.create({id: 1234}),
|
||||
category = Discourse.Category.list()[0];
|
||||
|
||||
topic.updateFromJson({
|
||||
|
@ -48,7 +50,7 @@ test("updateFromJson", function() {
|
|||
|
||||
test("destroy", function() {
|
||||
var user = Discourse.User.create({username: 'eviltrout'});
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
var topic = Topic.create({id: 1234});
|
||||
|
||||
sandbox.stub(Discourse, 'ajax');
|
||||
|
||||
|
@ -60,7 +62,7 @@ test("destroy", function() {
|
|||
|
||||
test("recover", function() {
|
||||
var user = Discourse.User.create({username: 'eviltrout'});
|
||||
var topic = Discourse.Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user});
|
||||
var topic = Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user});
|
||||
|
||||
sandbox.stub(Discourse, 'ajax');
|
||||
|
||||
|
|
Loading…
Reference in New Issue