Refactored loading routes for users/badges

This commit is contained in:
Robin Ward 2014-11-11 13:11:16 -05:00
parent af71326a9c
commit 4d4734ef2f
23 changed files with 253 additions and 234 deletions

View File

@ -3,6 +3,24 @@
var classify = Ember.String.classify;
var get = Ember.get;
var LOADING_WHITELIST = ['badges', 'userActivity', 'userPrivateMessages'],
_dummyRoute,
_loadingView;
function loadingResolver(cb) {
return function(parsedName) {
var fullNameWithoutType = parsedName.fullNameWithoutType;
if (fullNameWithoutType.indexOf('Loading') > 0) {
fullNameWithoutType = fullNameWithoutType.replace('Loading', '');
if (LOADING_WHITELIST.indexOf(fullNameWithoutType) !== -1) {
return cb(fullNameWithoutType);
} else {
Ember.warn('consider whitelisting a loading route for: ' + fullNameWithoutType);
}
}
};
}
function parseName(fullName) {
/*jshint validthis:true */
@ -66,7 +84,7 @@ export default Ember.DefaultResolver.extend({
},
resolveView: function(parsedName) {
return this.customResolve(parsedName) || this._super(parsedName);
return this.findLoadingView(parsedName) || this.customResolve(parsedName) || this._super(parsedName);
},
resolveHelper: function(parsedName) {
@ -82,16 +100,9 @@ export default Ember.DefaultResolver.extend({
},
resolveRoute: function(parsedName) {
return this.customResolve(parsedName) || this._super(parsedName);
return this.findLoadingRoute(parsedName) || this.customResolve(parsedName) || this._super(parsedName);
},
/**
Attaches a view and wires up the container properly
@method resolveTemplate
@param {String} parsedName the name of the template we want to resolve
@returns {Template} the template (if found)
**/
resolveTemplate: function(parsedName) {
return this.findPluginTemplate(parsedName) ||
this.findMobileTemplate(parsedName) ||
@ -99,6 +110,19 @@ export default Ember.DefaultResolver.extend({
Ember.TEMPLATES.not_found;
},
findLoadingRoute: loadingResolver(function() {
_dummyRoute = _dummyRoute || Ember.Route.extend();
return _dummyRoute;
}),
findLoadingView: loadingResolver(function() {
if (!_loadingView) {
_loadingView = require('discourse/views/loading', null, null, true /* force sync */);
if (_loadingView && _loadingView['default']) { _loadingView = _loadingView['default']; }
}
return _loadingView;
}),
findPluginTemplate: function(parsedName) {
var pluginParsedName = this.parseName(parsedName.fullName.replace("template:", "template:javascripts/"));
return this.findTemplate(pluginParsedName);

View File

@ -1,7 +1,7 @@
var spinnerHTML = "<div class='spinner'></div>";
Handlebars.registerHelper('loading-spinner', function() {
return new Handlebars.SafeString(spinnerHTML);
return new Handlebars.SafeString(spinnerHTML);
});
export { spinnerHTML };

View File

@ -13,6 +13,13 @@ export default Discourse.Route.extend({
}
},
afterModel: function(model) {
var self = this;
return Discourse.UserBadge.findByBadgeId(model.get('id')).then(function(userBadges) {
self.userBadges = userBadges;
});
},
titleToken: function() {
var model = this.modelFor('badges.show');
if (model) {
@ -21,10 +28,7 @@ export default Discourse.Route.extend({
},
setupController: function(controller, model) {
Discourse.UserBadge.findByBadgeId(model.get('id')).then(function(userBadges) {
controller.set('userBadges', userBadges);
controller.set('userBadgesLoaded', true);
});
controller.set('model', model);
controller.set('userBadges', this.userBadges);
}
});

View File

@ -4,7 +4,7 @@ export default Discourse.RestrictedUserRoute.extend({
},
renderTemplate: function() {
this.render({ into: 'user', outlet: 'userOutlet' });
this.render({ into: 'user' });
},
setupController: function(controller, model) {
@ -14,7 +14,7 @@ export default Discourse.RestrictedUserRoute.extend({
// A bit odd, but if we leave to /preferences we need to re-render that outlet
deactivate: function() {
this._super();
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
this.render('preferences', { into: 'user', controller: 'preferences' });
},
actions: {

View File

@ -4,13 +4,13 @@ export default Discourse.RestrictedUserRoute.extend({
},
renderTemplate: function() {
return this.render('user/badge-title', { into: 'user', outlet: 'userOutlet' });
return this.render('user/badge-title', { into: 'user' });
},
// A bit odd, but if we leave to /preferences we need to re-render that outlet
deactivate: function() {
this._super();
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
this.render('preferences', { into: 'user', controller: 'preferences' });
},
setupController: function(controller, model) {

View File

@ -4,13 +4,13 @@ export default Discourse.RestrictedUserRoute.extend({
},
renderTemplate: function() {
return this.render({ into: 'user', outlet: 'userOutlet' });
return this.render({ into: 'user' });
},
// A bit odd, but if we leave to /preferences we need to re-render that outlet
deactivate: function() {
this._super();
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
this.render('preferences', { into: 'user', controller: 'preferences' });
},
setupController: function(controller, model) {

View File

@ -4,7 +4,7 @@ export default Discourse.RestrictedUserRoute.extend({
},
renderTemplate: function() {
this.render({ into: 'user', outlet: 'userOutlet' });
this.render({ into: 'user' });
},
setupController: function(controller, model) {
@ -14,7 +14,7 @@ export default Discourse.RestrictedUserRoute.extend({
// A bit odd, but if we leave to /preferences we need to re-render that outlet
deactivate: function() {
this._super();
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
this.render('preferences', { into: 'user', controller: 'preferences' });
}
});

View File

@ -1,5 +1,5 @@
export default Discourse.RestrictedUserRoute.extend({
renderTemplate: function() {
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
this.render('preferences', { into: 'user', controller: 'preferences' });
}
});

View File

@ -4,13 +4,13 @@ export default Discourse.RestrictedUserRoute.extend({
},
renderTemplate: function() {
return this.render({ into: 'user', outlet: 'userOutlet' });
return this.render({ into: 'user' });
},
// A bit odd, but if we leave to /preferences we need to re-render that outlet
deactivate: function() {
this._super();
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
this.render('preferences', { into: 'user', controller: 'preferences' });
},
setupController: function(controller, user) {

View File

@ -16,6 +16,6 @@ export default Discourse.Route.extend({
},
renderTemplate: function() {
this.render('user/badges', {into: 'user', outlet: 'userOutlet'});
this.render('user/badges', {into: 'user'});
}
});

View File

@ -1,6 +1,6 @@
export default Discourse.Route.extend({
renderTemplate: function() {
this.render({ into: 'user', outlet: 'userOutlet' });
this.render({ into: 'user' });
},
model: function() {

View File

@ -0,0 +1 @@
export default Ember.Route.extend();

View File

@ -19,6 +19,6 @@ export default Discourse.Route.extend({
},
renderTemplate: function() {
this.render('user-notification-history', {into: 'user', outlet: 'userOutlet'});
this.render('user-notification-history', {into: 'user'});
}
});

View File

@ -1,12 +1,4 @@
/**
The base route for showing an activity stream.
@class UserActivityStreamRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.UserActivityStreamRoute = Discourse.Route.extend({
var UserActivityStreamRoute = Discourse.Route.extend({
model: function() {
return this.modelFor('user').get('stream');
},
@ -16,7 +8,7 @@ Discourse.UserActivityStreamRoute = Discourse.Route.extend({
},
renderTemplate: function() {
this.render('user_stream', {into: 'user', outlet: 'userOutlet'});
this.render('user_stream');
},
setupController: function(controller, model) {
@ -51,7 +43,7 @@ Discourse.UserActivityStreamRoute = Discourse.Route.extend({
// Build all activity stream routes
['bookmarks', 'edits', 'likes_given', 'likes_received', 'replies', 'posts', 'index'].forEach(function (userAction) {
Discourse["UserActivity" + userAction.classify() + "Route"] = Discourse.UserActivityStreamRoute.extend({
Discourse["UserActivity" + userAction.classify() + "Route"] = UserActivityStreamRoute.extend({
userActionType: Discourse.UserAction.TYPES[userAction]
});
});

View File

@ -14,7 +14,7 @@ function createAdminPostRoute (filter) {
},
renderTemplate: function() {
this.render("user/posts", { into: "user", outlet: "userOutlet" });
this.render("user/posts", { into: "user" });
}
});
}

View File

@ -1,6 +1,6 @@
Discourse.UserTopicListRoute = Discourse.Route.extend({
renderTemplate: function() {
this.render('user_topics_list', {into: 'user', outlet: 'userOutlet'});
this.render('user_topics_list');
},
setupController: function(controller, model) {

View File

@ -40,9 +40,5 @@
{{else}}
{{custom-html "footer"}}
{{/if}}
{{else}}
{{#unless userBadgesLoaded}}
{{loading-spinner}}
{{/unless}}
{{/if}}
</div>

View File

@ -1 +0,0 @@
{{loading-spinner}}

View File

@ -1 +1 @@
{{outlet activity}}
{{outlet}}

View File

@ -25,7 +25,3 @@
{{/grouped-each}}
</div>
{{/grouped-each}}
{{#if loading}}
{{loading-spinner}}
{{/if}}

View File

@ -3,198 +3,195 @@
{{global-notice}}
</div>
{{#unless loading}}
<div class="container">
<section class='user-main'>
<section {{bind-attr class="collapsedInfo :about profileBackground:has-background:no-background"}}>
<div class='staff-counters'>
{{#if number_of_flags_given}}
<div><span class="helpful-flags">{{number_of_flags_given}}</span>&nbsp;{{i18n user.staff_counters.flags_given}}</div>
{{/if}}
{{#if number_of_flagged_posts}}
<div>
{{#link-to 'user.flaggedPosts' this}}
<span class="flagged-posts">{{number_of_flagged_posts}}</span>&nbsp;{{i18n user.staff_counters.flagged_posts}}
{{/link-to}}
</div>
{{/if}}
{{#if number_of_deleted_posts}}
<div>
{{#link-to 'user.deletedPosts' this}}
<span class="deleted-posts">{{number_of_deleted_posts}}</span>&nbsp;{{i18n user.staff_counters.deleted_posts}}
{{/link-to}}
</div>
{{/if}}
{{#if number_of_suspensions}}
<div><span class="suspensions">{{number_of_suspensions}}</span>&nbsp;{{i18n user.staff_counters.suspensions}}</div>
{{/if}}
{{#if number_of_warnings}}
<div><span class="warnings-received">{{number_of_warnings}}</span>&nbsp;{{i18n user.staff_counters.warnings_received}}</div>
{{/if}}
</div>
<div class='profile-image' {{bind-attr style="profileBackground"}}></div>
<div class='details'>
<div class='primary'>
{{bound-avatar model "huge"}}
<section class='controls'>
<ul>
{{#if can_send_private_message_to_user}}
<li>
<a class='btn btn-primary right' {{action "composePrivateMessage"}}>
{{fa-icon "envelope"}}
{{i18n user.private_message}}
</a>
</li>
{{/if}}
{{#if viewingSelf}}
<li><a {{action "logout"}} class='btn btn-danger right'>{{fa-icon "sign-out"}}{{i18n user.log_out}}</a></li>
{{/if}}
{{#if currentUser.staff}}
<li><a {{bind-attr href="adminPath"}} class='btn right'>{{fa-icon "wrench"}}{{i18n admin.user.show_admin_profile}}</a></li>
{{/if}}
{{#if can_edit}}
<li>{{#link-to 'preferences' class="btn right"}}{{fa-icon "cog"}}{{i18n user.preferences}}{{/link-to}}</li>
{{/if}}
{{#if canInviteToForum}}
<li>{{#link-to 'user.invited' class="btn right"}}{{fa-icon "envelope-o"}}{{i18n user.invited.title}}{{/link-to}}</li>
{{/if}}
</ul>
</section>
<div class="primary-textual">
<h1>{{username}} {{{statusIcon}}}</h1>
<h2>{{name}}</h2>
<h3>
{{#if location}}{{fa-icon "map-maker"}}{{location}}{{/if}}
{{#if websiteName}}
{{fa-icon "globe"}}
{{#if linkWebsite}}
<a {{bind-attr href="website"}} rel="nofollow" target="_blank">{{websiteName}}</a>
{{else}}
<span {{bind-attr title="website"}}>{{websiteName}}</span>
{{/if}}
<div class="container">
<section class='user-main'>
<section {{bind-attr class="collapsedInfo :about profileBackground:has-background:no-background"}}>
<div class='staff-counters'>
{{#if number_of_flags_given}}
<div><span class="helpful-flags">{{number_of_flags_given}}</span>&nbsp;{{i18n user.staff_counters.flags_given}}</div>
{{/if}}
{{#if number_of_flagged_posts}}
<div>
{{#link-to 'user.flaggedPosts' this}}
<span class="flagged-posts">{{number_of_flagged_posts}}</span>&nbsp;{{i18n user.staff_counters.flagged_posts}}
{{/link-to}}
</div>
{{/if}}
{{#if number_of_deleted_posts}}
<div>
{{#link-to 'user.deletedPosts' this}}
<span class="deleted-posts">{{number_of_deleted_posts}}</span>&nbsp;{{i18n user.staff_counters.deleted_posts}}
{{/link-to}}
</div>
{{/if}}
{{#if number_of_suspensions}}
<div><span class="suspensions">{{number_of_suspensions}}</span>&nbsp;{{i18n user.staff_counters.suspensions}}</div>
{{/if}}
{{#if number_of_warnings}}
<div><span class="warnings-received">{{number_of_warnings}}</span>&nbsp;{{i18n user.staff_counters.warnings_received}}</div>
{{/if}}
</div>
<div class='profile-image' {{bind-attr style="profileBackground"}}></div>
<div class='details'>
<div class='primary'>
{{bound-avatar model "huge"}}
<section class='controls'>
<ul>
{{#if can_send_private_message_to_user}}
<li>
<a class='btn btn-primary right' {{action "composePrivateMessage"}}>
{{fa-icon "envelope"}}
{{i18n user.private_message}}
</a>
</li>
{{/if}}
</h3>
{{#if viewingSelf}}
<li><a {{action "logout"}} class='btn btn-danger right'>{{fa-icon "sign-out"}}{{i18n user.log_out}}</a></li>
{{/if}}
{{#if currentUser.staff}}
<li><a {{bind-attr href="adminPath"}} class='btn right'>{{fa-icon "wrench"}}{{i18n admin.user.show_admin_profile}}</a></li>
{{/if}}
{{#if can_edit}}
<li>{{#link-to 'preferences' class="btn right"}}{{fa-icon "cog"}}{{i18n user.preferences}}{{/link-to}}</li>
{{/if}}
{{#if canInviteToForum}}
<li>{{#link-to 'user.invited' class="btn right"}}{{fa-icon "envelope-o"}}{{i18n user.invited.title}}{{/link-to}}</li>
{{/if}}
</ul>
</section>
<div class='bio'>
{{#if isSuspended}}
<div class='suspended'>
{{fa-icon "ban"}}
<b>{{i18n user.suspended_notice date="suspendedTillDate"}}</b><br/>
<b>{{i18n user.suspended_reason}}</b> {{suspend_reason}}
</div>
{{/if}}
{{{bio_cooked}}}
</div>
{{plugin-outlet "user-profile-primary"}}
<div class="primary-textual">
<h1>{{username}} {{{statusIcon}}}</h1>
<h2>{{name}}</h2>
<h3>
{{#if location}}{{fa-icon "map-maker"}}{{location}}{{/if}}
{{#if websiteName}}
{{fa-icon "globe"}}
{{#if linkWebsite}}
<a {{bind-attr href="website"}} rel="nofollow" target="_blank">{{websiteName}}</a>
{{else}}
<span {{bind-attr title="website"}}>{{websiteName}}</span>
{{/if}}
{{/if}}
</h3>
<div class='bio'>
{{#if isSuspended}}
<div class='suspended'>
{{fa-icon "ban"}}
<b>{{i18n user.suspended_notice date="suspendedTillDate"}}</b><br/>
<b>{{i18n user.suspended_reason}}</b> {{suspend_reason}}
</div>
{{/if}}
{{{bio_cooked}}}
</div>
{{plugin-outlet "user-profile-primary"}}
</div>
<div style='clear: both'></div>
</div>
<div style='clear: both'></div>
</div>
<div class='secondary'>
<dl>
{{#if created_at}}
<dt>{{i18n user.created}}</dt><dd>{{bound-date created_at}}</dd>
{{/if}}
{{#if last_posted_at}}
<dt>{{i18n user.last_posted}}</dt><dd>{{bound-date last_posted_at}}</dd>
{{/if}}
{{#if last_seen_at}}
<dt>{{i18n user.last_seen}}</dt><dd>{{bound-date last_seen_at}}</dd>
{{/if}}
{{#if invited_by}}
<dt>{{i18n user.invited_by}}</dt><dd>{{#link-to 'user' invited_by}}{{invited_by.username}}{{/link-to}}</dd>
{{/if}}
<dt>{{i18n user.trust_level}}</dt><dd>{{trustLevel.name}}</dd>
{{#if canCheckEmails}}
<dt>{{i18n user.email.title}}</dt>
<dd {{bind-attr title="email"}}>
{{#if email}}
{{email}}
{{else}}
<button class="btn btn-primary" title="{{i18n admin.users.check_email.title}}" {{action "checkEmail" this}}>{{fa-icon "envelope-o"}} {{i18n admin.users.check_email.text}}</button>
{{/if}}
</dd>
{{/if}}
{{#if custom_groups}}
<dt>{{i18n groups.title count=custom_groups.length}}</dt>
<dd class='groups'>
{{#each custom_groups}}
<span>{{#link-to 'group' this class="group-link"}}{{name}}{{/link-to}}</span>
{{/each}}
</dd>
{{/if}}
</dl>
{{plugin-outlet "user-profile-secondary"}}
</div>
</section>
<section class='user-navigation'>
<ul class='action-list nav-stacked'>
{{activity-filter count=statsCountNonPM user=model userActionType=userActionType indexStream=indexStream}}
{{#each stat in statsExcludingPms}}
{{activity-filter content=stat user=model userActionType=userActionType indexStream=indexStream}}
{{/each}}
{{#if showBadges}}
{{#link-to 'user.badges' tagName="li"}}
{{#link-to 'user.badges'}}
<i class='glyph fa fa-certificate'></i>
{{i18n badges.title}}
<span class='count'>({{badge_count}})</span>
<span class='fa fa-chevron-right'></span>
{{/link-to}}
{{/link-to}}
<div class='secondary'>
<dl>
{{#if created_at}}
<dt>{{i18n user.created}}</dt><dd>{{bound-date created_at}}</dd>
{{/if}}
{{#if canSeeNotificationHistory}}
{{#link-to 'user.notifications' tagName="li"}}
{{#link-to 'user.notifications'}}
<i class='glyph fa fa-comment'></i>
{{i18n user.notifications}}
<span class='count'>({{notification_count}})</span>
<span class='fa fa-chevron-right'></span>
{{/link-to}}
{{/link-to}}
{{#if last_posted_at}}
<dt>{{i18n user.last_posted}}</dt><dd>{{bound-date last_posted_at}}</dd>
{{/if}}
</ul>
{{#if canSeePrivateMessages}}
<h3>{{fa-icon "envelope"}} {{i18n user.private_messages}}</h3>
<ul class='action-list nav-stacked'>
<li {{bind-attr class=":noGlyph privateMessagesActive:active"}}>
{{#link-to 'userPrivateMessages.index' model}}
{{i18n user.messages.all}}
{{#if hasPMs}}<span class='count'>({{private_messages_stats.all}})</span>{{/if}}
<span class='fa fa-chevron-right'></span>
{{/link-to}}
</li>
<li {{bind-attr class=":noGlyph privateMessagesMineActive:active"}}>
{{#link-to 'userPrivateMessages.mine' model}}
{{i18n user.messages.mine}}
{{#if hasStartedPMs}}<span class='count'>({{private_messages_stats.mine}})</span>{{/if}}
<span class='fa fa-chevron-right'></span>
{{/link-to}}
</li>
<li {{bind-attr class=":noGlyph privateMessagesUnreadActive:active"}}>
{{#link-to 'userPrivateMessages.unread' model}}
{{i18n user.messages.unread}}
{{#if hasUnreadPMs}}<span class='badge-notification unread-private-messages'>{{private_messages_stats.unread}}</span>{{/if}}
<span class='fa fa-chevron-right'></span>
{{/link-to}}
</li>
</ul>
{{/if}}
</section>
{{outlet userOutlet}}
{{#if last_seen_at}}
<dt>{{i18n user.last_seen}}</dt><dd>{{bound-date last_seen_at}}</dd>
{{/if}}
{{#if invited_by}}
<dt>{{i18n user.invited_by}}</dt><dd>{{#link-to 'user' invited_by}}{{invited_by.username}}{{/link-to}}</dd>
{{/if}}
<dt>{{i18n user.trust_level}}</dt><dd>{{trustLevel.name}}</dd>
{{#if canCheckEmails}}
<dt>{{i18n user.email.title}}</dt>
<dd {{bind-attr title="email"}}>
{{#if email}}
{{email}}
{{else}}
<button class="btn btn-primary" title="{{i18n admin.users.check_email.title}}" {{action "checkEmail" this}}>{{fa-icon "envelope-o"}} {{i18n admin.users.check_email.text}}</button>
{{/if}}
</dd>
{{/if}}
{{#if custom_groups}}
<dt>{{i18n groups.title count=custom_groups.length}}</dt>
<dd class='groups'>
{{#each custom_groups}}
<span>{{#link-to 'group' this class="group-link"}}{{name}}{{/link-to}}</span>
{{/each}}
</dd>
{{/if}}
</dl>
{{plugin-outlet "user-profile-secondary"}}
</div>
</section>
</div>
{{#if loadedAllItems}}
{{custom-html "footer"}}
{{/if}}
<section class='user-navigation'>
<ul class='action-list nav-stacked'>
{{activity-filter count=statsCountNonPM user=model userActionType=userActionType indexStream=indexStream}}
{{#each stat in statsExcludingPms}}
{{activity-filter content=stat user=model userActionType=userActionType indexStream=indexStream}}
{{/each}}
{{#if showBadges}}
{{#link-to 'user.badges' tagName="li"}}
{{#link-to 'user.badges'}}
<i class='glyph fa fa-certificate'></i>
{{i18n badges.title}}
<span class='count'>({{badge_count}})</span>
<span class='fa fa-chevron-right'></span>
{{/link-to}}
{{/link-to}}
{{/if}}
{{#if canSeeNotificationHistory}}
{{#link-to 'user.notifications' tagName="li"}}
{{#link-to 'user.notifications'}}
<i class='glyph fa fa-comment'></i>
{{i18n user.notifications}}
<span class='count'>({{notification_count}})</span>
<span class='fa fa-chevron-right'></span>
{{/link-to}}
{{/link-to}}
{{/if}}
</ul>
{{/unless}}
{{#if canSeePrivateMessages}}
<h3>{{fa-icon "envelope"}} {{i18n user.private_messages}}</h3>
<ul class='action-list nav-stacked'>
<li {{bind-attr class=":noGlyph privateMessagesActive:active"}}>
{{#link-to 'userPrivateMessages.index' model}}
{{i18n user.messages.all}}
{{#if hasPMs}}<span class='count'>({{private_messages_stats.all}})</span>{{/if}}
<span class='fa fa-chevron-right'></span>
{{/link-to}}
</li>
<li {{bind-attr class=":noGlyph privateMessagesMineActive:active"}}>
{{#link-to 'userPrivateMessages.mine' model}}
{{i18n user.messages.mine}}
{{#if hasStartedPMs}}<span class='count'>({{private_messages_stats.mine}})</span>{{/if}}
<span class='fa fa-chevron-right'></span>
{{/link-to}}
</li>
<li {{bind-attr class=":noGlyph privateMessagesUnreadActive:active"}}>
{{#link-to 'userPrivateMessages.unread' model}}
{{i18n user.messages.unread}}
{{#if hasUnreadPMs}}<span class='badge-notification unread-private-messages'>{{private_messages_stats.unread}}</span>{{/if}}
<span class='fa fa-chevron-right'></span>
{{/link-to}}
</li>
</ul>
{{/if}}
</section>
{{outlet}}
</section>
</div>
{{#if loadedAllItems}}
{{custom-html "footer"}}
{{/if}}

View File

@ -0,0 +1,7 @@
import { spinnerHTML } from 'discourse/helpers/loading-spinner';
export default Ember.View.extend({
render: function(buffer) {
buffer.push(spinnerHTML);
}
});

View File

@ -0,0 +1,3 @@
import LoadingView from 'discourse/views/loading';
export default LoadingView;