Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions public/openapi/components/schemas/TopicObject.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ TopicObject:
description: "`pinExpiry` rendered as an ISO 8601 format"
index:
type: number
endorsed:
type: boolean
required:
- tid
TopicObjectSlim:
Expand Down Expand Up @@ -305,9 +307,12 @@ TopicObjectSlim:
url:
type: string
description: Relative path to the topic thumbnail

- type: object
description: Optional properties that may or may not be present (except for `tid`, which is always present, and is only here as a hack to pass validation)
properties:
endorsed:
type: boolean
tid:
type: number
description: A topic identifier
Expand Down
9 changes: 9 additions & 0 deletions src/topics/endorsed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';
const posts = require('../posts');
module.exports = function (Topics) {
Topics.getEndorsedStatus = async (topics) => {
const mainPids = topics.map(topic => topic.mainPid);
const endorsers = await posts.getEndorsedUsers(mainPids);
return endorsers.map(endorsersForAPost => endorsersForAPost.length > 0);
};
};
6 changes: 5 additions & 1 deletion src/topics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require('./posts')(Topics);
require('./follow')(Topics);
require('./tags')(Topics);
require('./teaser')(Topics);
require('./endorsed')(Topics);
Topics.scheduled = require('./scheduled');
require('./suggested')(Topics);
require('./tools')(Topics);
Expand Down Expand Up @@ -95,13 +96,14 @@ Topics.getTopicsByTids = async function (tids, options) {
return data;
}

const [teasers, users, userSettings, categoriesData, guestHandles, thumbs] = await Promise.all([
const [teasers, users, userSettings, categoriesData, guestHandles, thumbs, isTopicEndorsed] = await Promise.all([
Topics.getTeasers(topics, options),
user.getUsersFields(uids, ['uid', 'username', 'fullname', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status']),
loadShowfullnameSettings(),
categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'backgroundImage', 'imageClass', 'bgColor', 'color', 'disabled']),
loadGuestHandles(),
Topics.thumbs.load(topics),
Topics.getEndorsedStatus(topics),
]);

users.forEach((userObj, idx) => {
Expand All @@ -118,6 +120,7 @@ Topics.getTopicsByTids = async function (tids, options) {
categoriesMap: _.zipObject(cids, categoriesData),
tidToGuestHandle: _.zipObject(guestTopics.map(t => t.tid), guestHandles),
thumbs,
isTopicEndorsed,
};
}

Expand All @@ -140,6 +143,7 @@ Topics.getTopicsByTids = async function (tids, options) {
topic.user.displayname = topic.user.username;
}
topic.teaser = result.teasers[i] || null;
topic.endorsed = result.isTopicEndorsed[i];
topic.isOwner = topic.uid === parseInt(uid, 10);
topic.ignored = followData[i].ignoring;
topic.followed = followData[i].following;
Expand Down
96 changes: 65 additions & 31 deletions vendor/nodebb-theme-harmony-2.1.35/scss/modules/topics-list.scss
Original file line number Diff line number Diff line change
@@ -1,40 +1,74 @@
ul.topics-list, ul.categories-list {
li {
&.deleted {
.meta, .topic-thumbs { visibility: hidden!important; }
opacity: 0.65;
}
ul.topics-list,
ul.categories-list {
li {
&.deleted {
.meta,
.topic-thumbs {
visibility: hidden !important;
}
opacity: 0.65;
}

&.selected {
background-color: mix($body-bg, $body-color, 90%);
[component="topic/select"] {
color: $success!important;
visibility: visible;
}
}
p {
margin: 0 !important;
}
&.selected {
background-color: mix($body-bg, $body-color, 90%);
[component="topic/select"] {
color: $success !important;
visibility: visible;
}
}
p {
margin: 0 !important;
}

// all other skins use link-color for unread titles
&.unread .title {
color: $link-color!important;
}
// all other skins use link-color for unread titles
&.unread .title {
color: $link-color !important;
}

.ui-sortable-handle {
cursor: move;
}
.ui-sortable-handle {
cursor: move;
}

// if only one thumb don't display
[data-numthumbs="1"] { display: none; }
}
// if only one thumb don't display
[data-numthumbs="1"] {
display: none;
}
}
}
[component="category/topic"] {
position: relative;
}
[component="category/topic/endorsed-decor"] {
.triangle {
position: absolute;
height: 100%;
width: 100%;
transform: translate(50%, 50%) rotate(45deg);
background-color: hsl(110, 90, 40);
}
position: absolute;

right: 0;
bottom: 0;
height: 90px;
width: 90px;
overflow: hidden;

i {
position: absolute;
right: 10px;
bottom: 10px;
color: white;
font-size: 20px;
}
}

// on default skin use primary color for unread titles
.skin-noskin {
ul.topics-list, ul.categories-list {
li.unread .title {
color: $primary!important;
}
}
ul.topics-list,
ul.categories-list {
li.unread .title {
color: $primary !important;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<meta itemprop="itemListOrder" content="descending" />
<meta itemprop="position" content="{increment(./index, "1")}" />
<a id="{./index}" data-index="{./index}" component="topic/anchor"></a>

<div class="d-flex p-0 col-12 col-lg-7 gap-2 gap-lg-3 pe-1 align-items-start {{{ if config.theme.mobileTopicTeasers }}}mb-2 mb-lg-0{{{ end }}}">
<div class="flex-shrink-0 position-relative">
<a class="d-inline-block text-decoration-none avatar-tooltip" title="{./user.displayname}" href="{{{ if ./user.userslug }}}{config.relative_path}/user/{./user.userslug}{{{ else }}}#{{{ end }}}">
Expand Down Expand Up @@ -84,7 +83,6 @@
</a>
{{{ end }}}
</div>

<div class="d-flex p-0 col-lg-5 col-12 align-content-stretch">
<div class="meta stats d-none d-lg-grid col-6 gap-1 pe-2 text-muted" style="grid-template-columns: {{{ if !reputation:disabled }}}1fr{{{ end }}} 1fr 1fr;">
{{{ if !reputation:disabled }}}
Expand Down Expand Up @@ -126,6 +124,12 @@
</div>
</div>
</div>
{{{ if topics.endorsed }}}
<div component="category/topic/endorsed-decor">
<div class="triangle"></div>
<i class="fa fa-solid fa-award"></i>
</div>
{{{end}}}
</li>
{{{end}}}
</ul>