FIX: /categories page issues
FIX: endless spinner when /categories is set to homepage and you click the home logo FIX: latest column should respect topic state for the current user (new, unread, etc.) FIX: post count should have heat colors applied based on like ratios FIX: Add "More" button at the bottom of the latest column UX: The topic count number in the categories panel should be slightly larger
This commit is contained in:
parent
78e8aa823d
commit
96b6d342cc
|
@ -1,3 +1,4 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import DiscoveryController from 'discourse/controllers/discovery';
|
||||
|
||||
export default DiscoveryController.extend({
|
||||
|
@ -9,36 +10,14 @@ export default DiscoveryController.extend({
|
|||
// this makes sure the composer isn't scoping to a specific category
|
||||
category: null,
|
||||
|
||||
actions: {
|
||||
|
||||
refresh() {
|
||||
// Don't refresh if we're still loading
|
||||
if (this.get('controllers.discovery.loading')) { return; }
|
||||
|
||||
// If we `send('loading')` here, due to returning true it bubbles up to the
|
||||
// router and ember throws an error due to missing `handlerInfos`.
|
||||
// Lesson learned: Don't call `loading` yourself.
|
||||
this.set('controllers.discovery.loading', true);
|
||||
|
||||
const CategoryList = require('discourse/models/category-list').default;
|
||||
const parentCategory = this.get('model.parentCategory');
|
||||
const promise = parentCategory ? CategoryList.listForParent(this.store, parentCategory) :
|
||||
CategoryList.list(this.store);
|
||||
|
||||
const self = this;
|
||||
promise.then(function(list) {
|
||||
self.set('model', list);
|
||||
self.send('loadingComplete');
|
||||
});
|
||||
}
|
||||
@computed
|
||||
canEdit() {
|
||||
return Discourse.User.currentProp('staff');
|
||||
},
|
||||
|
||||
canEdit: function() {
|
||||
return Discourse.User.currentProp('staff');
|
||||
}.property(),
|
||||
|
||||
latestTopicOnly: function() {
|
||||
return this.get('model.categories').find(c => c.get('featuredTopics.length') > 1) === undefined;
|
||||
}.property('model.categories.@each.featuredTopics.length')
|
||||
@computed("model.categories.@each.featuredTopics.length")
|
||||
latestTopicOnly() {
|
||||
return this.get("model.categories").find(c => c.get('featuredTopics.length') > 1) === undefined;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -154,15 +154,15 @@ const DiscourseURL = Ember.Object.extend({
|
|||
// Schedule a DOM cleanup event
|
||||
Em.run.scheduleOnce('afterRender', Discourse.Route, 'cleanDOM');
|
||||
|
||||
// TODO: Extract into rules we can inject into the URL handler
|
||||
if (this.navigatedToHome(oldPath, path, opts)) { return; }
|
||||
|
||||
if (oldPath === path) {
|
||||
// If navigating to the same path send an app event. Views can watch it
|
||||
// and tell their controllers to refresh
|
||||
this.appEvents.trigger('url:refresh');
|
||||
}
|
||||
|
||||
// TODO: Extract into rules we can inject into the URL handler
|
||||
if (this.navigatedToHome(oldPath, path, opts)) { return; }
|
||||
|
||||
return this.handleURL(path, opts);
|
||||
},
|
||||
|
||||
|
|
|
@ -71,11 +71,6 @@ const Topic = RestModel.extend({
|
|||
I18n.t('last_post') + ": " + longDate(this.get('bumpedAt'));
|
||||
}.property('bumpedAt'),
|
||||
|
||||
@computed('replyCount')
|
||||
replyTitle(count) {
|
||||
return I18n.t("posts_likes", { count });
|
||||
},
|
||||
|
||||
createdAt: function() {
|
||||
return new Date(this.get('created_at'));
|
||||
}.property('created_at'),
|
||||
|
|
|
@ -31,7 +31,11 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
|||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
TopicList.find("latest").then(result => model.set("topicList", result));
|
||||
model.set("loadingTopics", true);
|
||||
|
||||
TopicList.find("latest")
|
||||
.then(result => model.set("topicList", result))
|
||||
.finally(() => model.set("loadingTopics", false));
|
||||
|
||||
controller.set("model", model);
|
||||
|
||||
|
@ -44,6 +48,28 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
|||
},
|
||||
|
||||
actions: {
|
||||
|
||||
refresh() {
|
||||
const controller = this.controllerFor("discovery/categories");
|
||||
|
||||
// Don't refresh if we're still loading
|
||||
if (!controller || controller.get("loading")) { return; }
|
||||
|
||||
// If we `send('loading')` here, due to returning true it bubbles up to the
|
||||
// router and ember throws an error due to missing `handlerInfos`.
|
||||
// Lesson learned: Don't call `loading` yourself.
|
||||
controller.set("loading", true);
|
||||
|
||||
const parentCategory = this.get("model.parentCategory");
|
||||
const promise = parentCategory ? CategoryList.listForParent(this.store, parentCategory) :
|
||||
CategoryList.list(this.store);
|
||||
|
||||
promise.then(list => {
|
||||
this.setupController(controller, list);
|
||||
controller.send("loadingComplete");
|
||||
});
|
||||
},
|
||||
|
||||
createCategory() {
|
||||
const groups = this.site.groups,
|
||||
everyoneName = groups.findBy("id", 0).name;
|
||||
|
|
|
@ -47,50 +47,61 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each model.topicList.topics as |t|}}
|
||||
<tr>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="{{if t.archived 'archived'}}" data-topic-id={{unbound t.id}}>
|
||||
<td class="topic-poster">
|
||||
{{#with t.posters.lastObject.user as |lastPoster|}}
|
||||
{{#user-link user=lastPoster}}
|
||||
{{avatar lastPoster imageSize="large"}}
|
||||
{{/user-link}}
|
||||
{{/with}}
|
||||
</td>
|
||||
<td class="main-link">
|
||||
<tr>
|
||||
{{topic-status topic=t}}
|
||||
{{topic-link t}}
|
||||
{{#if t.unseen}}
|
||||
<span class="badge-notification new-topic"></span>
|
||||
{{/if}}
|
||||
</tr>
|
||||
<tr>
|
||||
{{category-link t.category}}
|
||||
{{#if t.tags}}
|
||||
{{#each t.visibleListTags as |tag|}}
|
||||
{{discourse-tag tag}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</tr>
|
||||
</td>
|
||||
<td class="topic-stats">
|
||||
<div class="topic-replies">
|
||||
<a href="{{t.lastPostUrl}}" title="{{t.replyTitle}}">{{number t.replyCount noTitle="true"}}</a>
|
||||
</div>
|
||||
<div class="topic-last-activity">
|
||||
<a href="{{t.lastPostUrl}}" title="{{t.bumpedAtTitle}}">{{format-date t.bumpedAt format="tiny" noTitle="true"}}</a>
|
||||
</div>
|
||||
</td>
|
||||
{{#if model.loadingTopics}}
|
||||
{{loading-spinner}}
|
||||
{{else}}
|
||||
{{#if model.topicList.topics}}
|
||||
{{#each model.topicList.topics as |t|}}
|
||||
<tr>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="{{if t.archived 'archived'}}" data-topic-id={{unbound t.id}}>
|
||||
<td class="topic-poster">
|
||||
{{#with t.posters.firstObject.user as |originalPoster|}}
|
||||
{{#user-link user=originalPoster}}
|
||||
{{avatar originalPoster imageSize="large"}}
|
||||
{{/user-link}}
|
||||
{{/with}}
|
||||
</td>
|
||||
<td class="main-link">
|
||||
<tr>
|
||||
{{topic-status topic=t}}
|
||||
{{topic-link t}}
|
||||
{{topic-post-badges newPosts=t.totalUnread unseen=t.unseen url=t.lastUnreadUrl}}
|
||||
</tr>
|
||||
<tr>
|
||||
{{category-link t.category}}
|
||||
{{#if t.tags}}
|
||||
{{#each t.visibleListTags as |tag|}}
|
||||
{{discourse-tag tag}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</tr>
|
||||
</td>
|
||||
<td class="topic-stats">
|
||||
{{raw "list/posts-count-column" topic=t tagName="div"}}
|
||||
<div class="topic-last-activity">
|
||||
<a href="{{t.lastPostUrl}}" title="{{t.bumpedAtTitle}}">{{format-date t.bumpedAt format="tiny" noTitle="true"}}</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
{{else}}
|
||||
{{loading-spinner}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
<tr class="more-topics">
|
||||
<td>
|
||||
<a href="/latest" class="btn pull-right">{{i18n "more"}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr class="no-topics">
|
||||
<td>
|
||||
<h3>{{i18n "topics.none.latest"}}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="clearfix"></div>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<{{view.tagName}} class='num posts-map posts {{view.likesHeat}}' title='{{view.title}}'>
|
||||
<a href class='posts-map badge-posts {{view.likesHeat}}'>{{number topic.replyCount}}</a>
|
||||
<a href class='posts-map badge-posts {{view.likesHeat}}'>{{number topic.replyCount noTitle="true"}}</a>
|
||||
</{{view.tagName}}>
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { fmt } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Object.extend({
|
||||
tagName: "td",
|
||||
ratio: function() {
|
||||
var likes = parseFloat(this.get('topic.like_count')),
|
||||
posts = parseFloat(this.get('topic.posts_count'));
|
||||
|
||||
@computed("topic.like_count", "topic.posts_count")
|
||||
ratio(likeCount, postCount) {
|
||||
const likes = parseFloat(likeCount);
|
||||
const posts = parseFloat(postCount);
|
||||
|
||||
if (posts < 10) { return 0; }
|
||||
|
||||
return (likes || 0) / posts;
|
||||
}.property(),
|
||||
},
|
||||
|
||||
title: function() {
|
||||
return I18n.messageFormat('posts_likes_MF', {
|
||||
count: this.get('topic.replyCount'),
|
||||
ratio: this.get('ratioText')
|
||||
}).trim();
|
||||
}.property(),
|
||||
@computed("topic.replyCount", "ratioText")
|
||||
title(count, ratio) {
|
||||
return I18n.messageFormat('posts_likes_MF', { count, ratio }).trim();
|
||||
},
|
||||
|
||||
ratioText: function() {
|
||||
var ratio = this.get('ratio');
|
||||
|
||||
var settings = Discourse.SiteSettings;
|
||||
if (ratio > settings.topic_post_like_heat_high) { return 'high'; }
|
||||
@computed("ratio")
|
||||
ratioText(ratio) {
|
||||
const settings = this.siteSettings;
|
||||
if (ratio > settings.topic_post_like_heat_high) { return 'high'; }
|
||||
if (ratio > settings.topic_post_like_heat_medium) { return 'med'; }
|
||||
if (ratio > settings.topic_post_like_heat_low) { return 'low'; }
|
||||
if (ratio > settings.topic_post_like_heat_low) { return 'low'; }
|
||||
return '';
|
||||
}.property(),
|
||||
},
|
||||
|
||||
likesHeat: fmt('ratioText', 'heatmap-%@'),
|
||||
});
|
||||
|
|
|
@ -124,12 +124,24 @@ html.anon .topic-list a.title:visited:not(.badge-notification) {color: dark-ligh
|
|||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%));
|
||||
}
|
||||
}
|
||||
.topic-replies {
|
||||
font-weight: bold;
|
||||
.num.posts {
|
||||
text-align: right;
|
||||
a {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.posts-map.posts {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
.badge-posts {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.topic-list-latest {
|
||||
margin-left: 4%;
|
||||
.new-posts {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.topic-list.categories {
|
||||
th.stats {
|
||||
|
@ -138,6 +150,16 @@ html.anon .topic-list a.title:visited:not(.badge-notification) {color: dark-ligh
|
|||
.stats {
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
.value {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.no-topics, .more-topics {
|
||||
border-bottom: none;
|
||||
td {
|
||||
padding-right: 0 !important;
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1936,10 +1936,6 @@ en:
|
|||
posts: "Posts"
|
||||
posts_long: "there are {{number}} posts in this topic"
|
||||
|
||||
posts_likes:
|
||||
one: "This topic has 1 reply."
|
||||
other: "This topic has {{count}} replies."
|
||||
|
||||
# keys ending with _MF use message format, see https://meta.discourse.org/t/message-format-support-for-localization/7035 for details
|
||||
posts_likes_MF: |
|
||||
This topic has {count, plural, one {1 reply} other {# replies}} {ratio, select,
|
||||
|
|
Loading…
Reference in New Issue