FEATURE: Decorate category hashtag links.
This commit is contained in:
parent
0812807a53
commit
4a7f560a35
|
@ -3,6 +3,7 @@ import loadScript from 'discourse/lib/load-script';
|
||||||
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
|
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";
|
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";
|
||||||
import Category from 'discourse/models/category';
|
import Category from 'discourse/models/category';
|
||||||
|
import { SEPARATOR as categoryHashtagSeparator } from 'discourse/lib/category-hashtags';
|
||||||
|
|
||||||
// Our head can be a static string or a function that returns a string
|
// Our head can be a static string or a function that returns a string
|
||||||
// based on input (like for numbered lists).
|
// based on input (like for numbered lists).
|
||||||
|
@ -255,7 +256,7 @@ export default Ember.Component.extend({
|
||||||
template: template,
|
template: template,
|
||||||
key: '#',
|
key: '#',
|
||||||
transformComplete(category) {
|
transformComplete(category) {
|
||||||
return Category.slugFor(category, ":");
|
return Category.slugFor(category, categoryHashtagSeparator);
|
||||||
},
|
},
|
||||||
dataSource(term) {
|
dataSource(term) {
|
||||||
return Category.search(term);
|
return Category.search(term);
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function categoryBadgeHTML(category, opts) {
|
||||||
|
|
||||||
var description = get(category, 'description_text'),
|
var description = get(category, 'description_text'),
|
||||||
restricted = get(category, 'read_restricted'),
|
restricted = get(category, 'read_restricted'),
|
||||||
url = Discourse.getURL("/c/") + Discourse.Category.slugFor(category),
|
url = opts.url ? opts.url : Discourse.getURL("/c/") + Discourse.Category.slugFor(category),
|
||||||
href = (opts.link === false ? '' : url),
|
href = (opts.link === false ? '' : url),
|
||||||
tagName = (opts.link === false || opts.link === "false" ? 'span' : 'a'),
|
tagName = (opts.link === false || opts.link === "false" ? 'span' : 'a'),
|
||||||
extraClasses = (opts.extraClasses ? (' ' + opts.extraClasses) : ''),
|
extraClasses = (opts.extraClasses ? (' ' + opts.extraClasses) : ''),
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Category from 'discourse/models/category';
|
||||||
|
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||||
|
|
||||||
|
export const SEPARATOR = ":";
|
||||||
|
|
||||||
|
export function findCategoryByHashtagSlug(hashtagSlug) {
|
||||||
|
if (hashtagSlug.indexOf('#') === 0) hashtagSlug = hashtagSlug.slice(1);
|
||||||
|
return Category.findBySlug.apply(null, hashtagSlug.split(SEPARATOR, 2).reverse());
|
||||||
|
};
|
||||||
|
|
||||||
|
export function replaceSpan($elem, categorySlug, categoryLink) {
|
||||||
|
const category = findCategoryByHashtagSlug(categorySlug);
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
$elem.replaceWith(categorySlug);
|
||||||
|
} else {
|
||||||
|
$elem.replaceWith(categoryBadgeHTML(
|
||||||
|
category, { url: categoryLink, allowUncategorized: true }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function decorateLinks($elems) {
|
||||||
|
$elems.each((_, elem) => replaceSpan($(elem), elem.text, elem.href));
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
|
import { replaceSpan } from 'discourse/lib/category-hashtags';
|
||||||
|
|
||||||
const validCategoryHashtags = {};
|
const validCategoryHashtags = {};
|
||||||
const checkedCategoryHashtags = [];
|
const checkedCategoryHashtags = [];
|
||||||
const testedKey = 'tested';
|
const testedKey = 'tested';
|
||||||
const testedClass = `hashtag-${testedKey}`;
|
const testedClass = `hashtag-${testedKey}`;
|
||||||
|
|
||||||
function replaceSpan($elem, categorySlug, categoryLink) {
|
|
||||||
$elem.replaceWith(`<a href="${categoryLink}" class="hashtag">#${categorySlug}</a>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFound($hashtags, categorySlugs) {
|
function updateFound($hashtags, categorySlugs) {
|
||||||
Ember.run.schedule('afterRender', () => {
|
Ember.run.schedule('afterRender', () => {
|
||||||
$hashtags.each((index, hashtag) => {
|
$hashtags.each((index, hashtag) => {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { number } from 'discourse/lib/formatter';
|
||||||
import DiscourseURL from 'discourse/lib/url';
|
import DiscourseURL from 'discourse/lib/url';
|
||||||
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
|
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
|
||||||
import { fmt } from 'discourse/lib/computed';
|
import { fmt } from 'discourse/lib/computed';
|
||||||
|
import { decorateLinks as decorateCategoryHashtagLinks } from 'discourse/lib/category-hashtags';
|
||||||
|
|
||||||
const DAY = 60 * 50 * 1000;
|
const DAY = 60 * 50 * 1000;
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
||||||
_cookedWasChanged() {
|
_cookedWasChanged() {
|
||||||
this.trigger('postViewUpdated', this.$());
|
this.trigger('postViewUpdated', this.$());
|
||||||
this._insertQuoteControls();
|
this._insertQuoteControls();
|
||||||
|
this._decorateCategoryHashtagLinks();
|
||||||
},
|
},
|
||||||
|
|
||||||
mouseUp(e) {
|
mouseUp(e) {
|
||||||
|
@ -318,6 +320,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
||||||
const $post = this.$(),
|
const $post = this.$(),
|
||||||
postNumber = this.get('post').get('post_number');
|
postNumber = this.get('post').get('post_number');
|
||||||
|
|
||||||
|
this._decorateCategoryHashtagLinks();
|
||||||
this._showLinkCounts();
|
this._showLinkCounts();
|
||||||
|
|
||||||
ScreenTrack.current().track($post.prop('id'), postNumber);
|
ScreenTrack.current().track($post.prop('id'), postNumber);
|
||||||
|
@ -375,7 +378,12 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
||||||
cooked.unhighlight();
|
cooked.unhighlight();
|
||||||
this._highlighted = false;
|
this._highlighted = false;
|
||||||
}
|
}
|
||||||
}.observes('searchService.highlightTerm', 'cooked')
|
}.observes('searchService.highlightTerm', 'cooked'),
|
||||||
|
|
||||||
|
_decorateCategoryHashtagLinks() {
|
||||||
|
const $elems = this.$('.cooked a.hashtag');
|
||||||
|
if ($elems.length > 0) decorateCategoryHashtagLinks($elems);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PostView;
|
export default PostView;
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
color: $primary !important;
|
color: $primary !important;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
vertical-align: text-top;
|
vertical-align: text-top;
|
||||||
margin-top: -3px; //vertical alignment fix
|
margin-top: -2px; //vertical alignment fix
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -38,21 +38,18 @@
|
||||||
.extra-info-wrapper & {
|
.extra-info-wrapper & {
|
||||||
color: $header-primary !important;
|
color: $header-primary !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-category-parent-bg, .badge-category-bg {
|
.badge-category-parent-bg, .badge-category-bg {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "\a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "\a0";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&.bullet { //bullet category style
|
&.bullet { //bullet category style
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
@ -71,31 +68,30 @@
|
||||||
.extra-info-wrapper & {
|
.extra-info-wrapper & {
|
||||||
color: $header-primary !important;
|
color: $header-primary !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-category-parent-bg, .badge-category-bg {
|
.badge-category-parent-bg, .badge-category-bg {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "\a0";
|
content: "\a0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
&.badge-category-parent-bg { //subcategory style
|
&.badge-category-parent-bg { //subcategory style
|
||||||
width: 5px;
|
width: 5px;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
& + .badge-category-bg {
|
& + .badge-category-bg {
|
||||||
width: 5px;
|
width: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&.box { //box category style (apply custom widths to the wrapper, not the children)
|
&.box { //box category style (apply custom widths to the wrapper, not the children)
|
||||||
|
@ -134,6 +130,59 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin cooked-badge-bullet($length, $offset:0px) {
|
||||||
|
.badge-wrapper.bullet {
|
||||||
|
span {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.badge-category-bg {
|
||||||
|
width: $length;
|
||||||
|
height: $length;
|
||||||
|
top: $offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.badge-category-parent-bg {
|
||||||
|
width: $length / 2;
|
||||||
|
height: $length;
|
||||||
|
top: $offset;
|
||||||
|
|
||||||
|
& + .badge-category-bg {
|
||||||
|
width: $length / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cooked, .d-editor-preview {
|
||||||
|
p .badge-wrapper.bullet {
|
||||||
|
margin: 0px 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { @include cooked-badge-bullet(22px) }
|
||||||
|
h2 { @include cooked-badge-bullet(17px) }
|
||||||
|
h3 { @include cooked-badge-bullet(14px) }
|
||||||
|
h4 { @include cooked-badge-bullet(12px) }
|
||||||
|
h5 { @include cooked-badge-bullet(10px, -1.1px) }
|
||||||
|
h6 { @include cooked-badge-bullet(9px, -1.5px) }
|
||||||
|
|
||||||
|
.badge-wrapper.box {
|
||||||
|
span {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-notification.clicks {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: visible;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-category-bg {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Category badge dropdown
|
// Category badge dropdown
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import createStore from 'helpers/create-store';
|
||||||
|
import Category from 'discourse/models/category';
|
||||||
|
import { findCategoryByHashtagSlug } from "discourse/lib/category-hashtags";
|
||||||
|
|
||||||
|
module("lib:category-hashtags");
|
||||||
|
|
||||||
|
test('findCategoryByHashtagSlug', () => {
|
||||||
|
const store = createStore();
|
||||||
|
|
||||||
|
const parentCategory = store.createRecord('category', { slug: 'test1' });
|
||||||
|
|
||||||
|
const childCategory = store.createRecord('category', {
|
||||||
|
slug: 'test2', parentCategory: parentCategory
|
||||||
|
});
|
||||||
|
|
||||||
|
sandbox.stub(Category, 'list').returns([parentCategory, childCategory]);
|
||||||
|
|
||||||
|
equal(findCategoryByHashtagSlug('test1'), parentCategory, 'returns the right category');
|
||||||
|
equal(findCategoryByHashtagSlug('test1:test2'), childCategory, 'returns the right category');
|
||||||
|
equal(findCategoryByHashtagSlug('#test1'), parentCategory, 'returns the right category');
|
||||||
|
equal(findCategoryByHashtagSlug('#test1:test2'), childCategory, 'returns the right category');
|
||||||
|
});
|
Loading…
Reference in New Issue