mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-07 14:35:47 +01:00
* chore: up deps * chore: up composer * fix(deps): bump 2factor to v7 * chore: up harmony * chore: up harmony * fix: missing await * feat: allow middlewares to pass in template values via res.locals * feat: buildAccountData middleware automatically added ot all account routes * fix: properly allow values in res.locals.templateValues to be added to the template data * refactor: user/blocks * refactor(accounts): categories and consent * feat: automatically 404 if exposeUid or exposeGroupName come up empty * refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now * fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization * fix: move reputation removal check to accountHelpers method * test: skip i18n tests if ref branch when present is not develop * fix(deps): bump theme versions * fix(deps): bump ntfy and 2factor * chore: up harmony * fix: add missing return * fix: #11191, only focus on search input on md environments and up * feat: allow file uploads on mobile chat closes https://github.com/NodeBB/NodeBB/issues/11217 * chore: up themes * chore: add lang string * fix(deps): bump ntfy to 1.0.15 * refactor: use new if/each syntax * chore: up composer * fix: regression from user helper refactor * chore: up harmony * chore: up composer * chore: up harmony * chore: up harmony * chore: up harmony * chore: fix composer version * feat: add increment helper * chore: up harmony * fix: #11228 no timestamps in future ⌛ * chore: up harmony * check config.theme as well fire action:posts.loaded after processing dom * chore: up harmony * chore: up harmony * chore: up harmony * chore: up themes * chore: up harmony * remove extra class * refactor: move these to core from harmony * chore: up widgets * chore: up widgets * height auto * fix: closes #11238 * dont focus inputs, annoying on mobile * fix: dont focus twice, only focus on chat input on desktop dont wrap widget footer in row * chore: up harmony * chore: up harmony * update chat window * chore: up themes * fix cache buster for skins * chat fixes * chore: up harmony * chore: up composer * refactor: change hook logs to debug * fix: scroll to post right after adding to dom * fix: hash scrolling and highlighting correct post * test: re-enable read API schema tests * fix: add back schema changes for179faa2270andc3920ccb10* fix: schema changes from488f0978a4* fix: schema changes forf4cf482a87* fix: schema update forbe6bbabd0e* fix: schema changes for69c96078ea* fix: schema changes ford1364c3130* fix: schema changes for84ff1152f7* fix: schema changes forb860c2605c* fix: schema changes for23cb67a112* fix: schema changes forb916e42f40* fix: schema change fora9bbb586fc* fix: schema changes for4b738c8cd3* fix: schema changes for58b5781cea* fix: schema changes for794bf01b21* fix: schema changes for80ea12c1c1,e368feef51, and52ead114be* fix: composer-default object in config? * fix: schema changes for9acdc6808cand0930934200* fix: schema changes forc0a52924f1* fix: schema change foraba420a3f3, move loggedInUser to optional props * fix: schema changes for8c67031609* fix: schema changes for27e53b42f3* fix: schema changes for2835966518* fix: breaking test for email confirmation API call * fix: schema changes for refactored search page * fix: schema changes for user object * fix: schema changes for9f531f957e* fix: schema changes forc4042c70deand23175110a2* fix: schema changes for9b3616b103* fix: schema changes for5afd5de07d* fix: schema change for1d7baf1217* fix: schema changes for57bfb37c55andbe6bbabd0e* fix: schema changes for6e86b4afa2and3efad2e13band68f66223e7* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before) * fix: re-login on email change * fix: schema changes forc926358d73* fix: schema changes for388a8270c9* fix: schema change for2658bcc821* fix: no need to call account middlewares for chats routes * fix: schema changes for71743affc3* fix: final schema changes * test: support for anyOf and oneOf * fix: check thumb * dont scroll to top on back press * remove group log * fix: add top margin to merged and deleted alerts * chore: up widgets * fix: improve fix-lists mixin * chore: up harmony/composer * feat: allow hiding quicksearch results during search * dont record searches made by composer * chore: up 54 * chore: up spam be gone * feat: add prev/next page and page count into mobile paginator * chore: up harmony * chore: up harmony * use old style for IS * fix: hide entire toolbar row if no posts or not singlePost * fix: updated messaging for post-queue template, #11206 * fix: btn-sm on post queue back button * fix: bump harmony, closes #11206 * fix: remove unused alert module import * fix: bump harmony * fix: bump harmony * chore: up harmony * refactor: IS scrolltop * fix: update users:search-user-for-chat source string * feat: support for mark-read toggle on chats dropdown and recent chats list * feat: api v3 calls to mark chat read/unread * feat: send event:chats.mark socket event on mark read or unread * refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling * docs: openapi schema updates for chat marking * fix: allow unread state toggling in chats dropdown too * fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread * fix: debug log * refactor: move userSearch filter to a module * feat(routes): allow remounting /categories (#11230) * feat: send flags count to frontend on flags list page * refactor: filter form client-side js to extract out some logic * fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden * fix: use userFilter module for assignee, reporterId, targetUid * fix(openapi): schema changes for updated flags page * fix: dont allow adding duplicates to userFilter * use same var * remove log * fix: closes #11282 * feat: lang key for x-topics * chore: up harmony * chore: up emoji * chore: up harmony * fix: update userFilter to allow new option `selectedBlock` * fix: wrong block name passed to userFilter * fix: https://github.com/NodeBB/NodeBB/issues/11283 * fix: chats, allow multiple dropdowns like in harmony * chore: up harmony * refactor: flag note adding/editing, closes #11285 * fix: remove old prepareEdit logic * chore: add caveat about hacky code block in userFilter module * fix: placeholders for userFilter module * refactor: navigator so it works with multiple thumbs/navigators * chore: up harmony * fix: closes #11287, destroy quick reply autocomplete on navigation * fix: filter disabled categories on user categories page count * chore: up harmony * docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying * fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests * fix: tweak table order in ACP dash searches * fix: only invoke navigator click drag on left mouse button * feat: add back unread indicator to navigator * clear bookmark on mark unread * fix: navigator crash on ajaxify * better thumb top calculation * fix: reset user bookmark when topic is marked unread * Revert "fix: reset user bookmark when topic is marked unread" This reverts commit9bcd85c2c6. * fix: update unread indicator on scroll, add unread count * chore: bump harmony * fix: crash on navigator unread update when backing out of a topic * fix: closes #11183 * fix: update topics:recent zset when rescheduling a topic * fix: dupe quote button, increase delay, hide immediately on empty selection * fix: navigator not showing up on first load * refactor: remove glance assorted fixes to navigator dont reduce remaning count if user scrolls down and up quickly only call topic.navigatorCallback when index changes * more sanity checks for bookmark dont allow setting bookmark higher than topic postcount * closes #11218, 🚋 * Revert "fix: update topics:recent zset when rescheduling a topic" This reverts commit737973cca9. * fix: #11306, show proper error if queued post doesn't exist was showing no-privileges if someone else accepted the post * https://github.com/NodeBB/NodeBB/issues/11307 dont use li * chore: up harmony * chore: bump version string * fix: copy paste fail * feat: closes #7382, tag filtering add client side support for filtering by tags on /category, /recent and /unread * chore: up harmony * chore: up harmony * Revert "fix: add back req.query fallback for backwards compatibility" [breaking] This reverts commitcf6cc2c454. This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x This is a breaking change. * fix: pass csrf token in form data, re: NodeBB/NodeBB#11309 * chore: up deps * fix: tests, use x-csrf-token query param removed * test: fix csrf_token * lint: remove unused * feat: add itemprop="image" to avatar helper * fix: get chat upload button in chat modal * breaking: remove deprecated socket.io methods * test: update messaging tests to not use sockets * fix: parent post links * fix: prevent post tooltip if mouse leaves before data/tpl is loaded * chore: up harmony * chore: up harmony * chore: up harmony * chore: up harmony * fix: nested replies indices * fix(deps): bump 2factor * feat: add loggedIn user to all api routes * chore: up themes * refactor: audit admin v3 write api routes as per #11321 * refactor: audit category v3 write api routes as per #11321 [breaking] docs: fix open api spec for #11321 * refactor: audit chat v3 write api routes as per #11321 * refactor: audit files v3 write api routes as per #11321 * refactor: audit flags v3 write api routes as per #11321 * refactor: audit posts v3 write api routes as per #11321 * refactor: audit topics v3 write api routes as per #11321 * refactor: audit users v3 write api routes as per #11321 * fix: lang string * remove min height * fix: empty topic/labels taking up space * fix: tag filtering when changing filter to watched topics or changing popular time limit to month * chore: up harmony * fix: closes #11354, show no post error if queued post already accepted/rejected * test: #11354 * test: #11354 * fix(deps): bump 2factor * fix: #11357 clear cache on thumb remove * fix: thumb remove on windows, closes #11357 * test: openapi for thumbs * test: fix openapi --------- Co-authored-by: Julian Lam <julian@nodebb.org> Co-authored-by: Opliko <opliko.reg@protonmail.com>
422 lines
13 KiB
JavaScript
422 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
|
|
define('forum/topic/threadTools', [
|
|
'components',
|
|
'translator',
|
|
'handleBack',
|
|
'forum/topic/posts',
|
|
'api',
|
|
'hooks',
|
|
'bootbox',
|
|
'alerts',
|
|
'bootstrap',
|
|
], function (components, translator, handleBack, posts, api, hooks, bootbox, alerts, bootstrap) {
|
|
const ThreadTools = {};
|
|
|
|
ThreadTools.init = function (tid, topicContainer) {
|
|
renderMenu(topicContainer);
|
|
|
|
$('.topic-main-buttons [title]').tooltip({
|
|
container: '#content',
|
|
animation: false,
|
|
});
|
|
|
|
observeTopicLabels();
|
|
|
|
// function topicCommand(method, path, command, onComplete) {
|
|
topicContainer.on('click', '[component="topic/delete"]', function () {
|
|
topicCommand('del', '/state', 'delete');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/restore"]', function () {
|
|
topicCommand('put', '/state', 'restore');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/purge"]', function () {
|
|
topicCommand('del', '', 'purge');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/lock"]', function () {
|
|
topicCommand('put', '/lock', 'lock');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/unlock"]', function () {
|
|
topicCommand('del', '/lock', 'unlock');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/pin"]', function () {
|
|
topicCommand('put', '/pin', 'pin');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/unpin"]', function () {
|
|
topicCommand('del', '/pin', 'unpin');
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/event/delete"]', function () {
|
|
const eventId = $(this).attr('data-topic-event-id');
|
|
const eventEl = $(this).parents('[component="topic/event"]');
|
|
bootbox.confirm('[[topic:delete-event-confirm]]', (ok) => {
|
|
if (ok) {
|
|
api.del(`/topics/${tid}/events/${eventId}`, {})
|
|
.then(function () {
|
|
eventEl.remove();
|
|
})
|
|
.catch(alerts.error);
|
|
}
|
|
});
|
|
});
|
|
|
|
// todo: should also use topicCommand, but no write api call exists for this yet
|
|
topicContainer.on('click', '[component="topic/mark-unread"]', function () {
|
|
socket.emit('topics.markUnread', tid, function (err) {
|
|
if (err) {
|
|
return alerts.error(err);
|
|
}
|
|
|
|
if (app.previousUrl && !app.previousUrl.match('^/topic')) {
|
|
ajaxify.go(app.previousUrl, function () {
|
|
handleBack.onBackClicked(true);
|
|
});
|
|
} else if (ajaxify.data.category) {
|
|
ajaxify.go('category/' + ajaxify.data.category.slug, handleBack.onBackClicked);
|
|
}
|
|
|
|
alerts.success('[[topic:mark_unread.success]]');
|
|
});
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/mark-unread-for-all"]', function () {
|
|
const btn = $(this);
|
|
socket.emit('topics.markAsUnreadForAll', [tid], function (err) {
|
|
if (err) {
|
|
return alerts.error(err);
|
|
}
|
|
alerts.success('[[topic:markAsUnreadForAll.success]]');
|
|
btn.parents('.thread-tools.open').find('.dropdown-toggle').trigger('click');
|
|
});
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/move"]', function () {
|
|
require(['forum/topic/move'], function (move) {
|
|
move.init([tid], ajaxify.data.cid);
|
|
});
|
|
return false;
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/delete/posts"]', function () {
|
|
require(['forum/topic/delete-posts'], function (deletePosts) {
|
|
deletePosts.init();
|
|
});
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/fork"]', function () {
|
|
require(['forum/topic/fork'], function (fork) {
|
|
fork.init();
|
|
});
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/merge"]', function () {
|
|
require(['forum/topic/merge'], function (merge) {
|
|
merge.init(function () {
|
|
merge.addTopic(ajaxify.data.tid);
|
|
});
|
|
});
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/move-posts"]', function () {
|
|
require(['forum/topic/move-post'], function (movePosts) {
|
|
movePosts.init();
|
|
});
|
|
});
|
|
|
|
topicContainer.on('click', '[component="topic/following"]', function () {
|
|
changeWatching('follow');
|
|
});
|
|
topicContainer.on('click', '[component="topic/not-following"]', function () {
|
|
changeWatching('follow', 0);
|
|
});
|
|
topicContainer.on('click', '[component="topic/ignoring"]', function () {
|
|
changeWatching('ignore');
|
|
});
|
|
|
|
function changeWatching(type, state = 1) {
|
|
const method = state ? 'put' : 'del';
|
|
api[method](`/topics/${tid}/${type}`, {}, () => {
|
|
let message = '';
|
|
if (type === 'follow') {
|
|
message = state ? '[[topic:following_topic.message]]' : '[[topic:not_following_topic.message]]';
|
|
} else if (type === 'ignore') {
|
|
message = state ? '[[topic:ignoring_topic.message]]' : '[[topic:not_following_topic.message]]';
|
|
}
|
|
|
|
// From here on out, type changes to 'unfollow' if state is falsy
|
|
if (!state) {
|
|
type = 'unfollow';
|
|
}
|
|
|
|
setFollowState(type);
|
|
|
|
alerts.alert({
|
|
alert_id: 'follow_thread',
|
|
message: message,
|
|
type: 'success',
|
|
timeout: 5000,
|
|
});
|
|
|
|
hooks.fire('action:topics.changeWatching', { tid: tid, type: type });
|
|
}, () => {
|
|
alerts.alert({
|
|
type: 'danger',
|
|
alert_id: 'topic_follow',
|
|
title: '[[global:please_log_in]]',
|
|
message: '[[topic:login_to_subscribe]]',
|
|
timeout: 5000,
|
|
});
|
|
});
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
function observeTopicLabels() {
|
|
// show or hide topic/labels container depending on children visibility
|
|
const labels = $('[component="topic/labels"]');
|
|
const mut = new MutationObserver(function (mutations) {
|
|
const first = mutations[0];
|
|
if (first && first.attributeName === 'class') {
|
|
const visibleChildren = labels.children().filter((index, el) => !$(el).hasClass('hidden'));
|
|
labels.toggleClass('hidden', !visibleChildren.length);
|
|
}
|
|
});
|
|
|
|
labels.children().each((index, el) => {
|
|
mut.observe(el, { attributes: true });
|
|
});
|
|
}
|
|
|
|
function renderMenu(container) {
|
|
container = container.get(0);
|
|
if (!container) {
|
|
return;
|
|
}
|
|
|
|
container.querySelectorAll('.thread-tools').forEach((toolsEl) => {
|
|
toolsEl.addEventListener('show.bs.dropdown', (e) => {
|
|
const dropdownMenu = e.target.nextElementSibling;
|
|
if (!dropdownMenu) {
|
|
return;
|
|
}
|
|
|
|
socket.emit('topics.loadTopicTools', { tid: ajaxify.data.tid, cid: ajaxify.data.cid }, function (err, data) {
|
|
if (err) {
|
|
return alerts.error(err);
|
|
}
|
|
app.parseAndTranslate('partials/topic/topic-menu-list', data, function (html) {
|
|
$(dropdownMenu).html(html);
|
|
hooks.fire('action:topic.tools.load', {
|
|
element: $(dropdownMenu),
|
|
});
|
|
});
|
|
});
|
|
}, {
|
|
once: true,
|
|
});
|
|
});
|
|
}
|
|
|
|
function topicCommand(method, path, command, onComplete) {
|
|
if (!onComplete) {
|
|
onComplete = function () {};
|
|
}
|
|
const tid = ajaxify.data.tid;
|
|
const body = {};
|
|
const execute = function (ok) {
|
|
if (ok) {
|
|
api[method](`/topics/${tid}${path}`, body)
|
|
.then(onComplete)
|
|
.catch(alerts.error);
|
|
}
|
|
};
|
|
|
|
switch (command) {
|
|
case 'delete':
|
|
case 'restore':
|
|
case 'purge':
|
|
bootbox.confirm(`[[topic:thread_tools.${command}_confirm]]`, execute);
|
|
break;
|
|
|
|
case 'pin':
|
|
ThreadTools.requestPinExpiry(body, execute.bind(null, true));
|
|
break;
|
|
|
|
default:
|
|
execute(true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ThreadTools.requestPinExpiry = function (body, onSuccess) {
|
|
app.parseAndTranslate('modals/set-pin-expiry', {}, function (html) {
|
|
const modal = bootbox.dialog({
|
|
title: '[[topic:thread_tools.pin]]',
|
|
message: html,
|
|
onEscape: true,
|
|
size: 'small',
|
|
buttons: {
|
|
cancel: {
|
|
label: '[[modules:bootbox.cancel]]',
|
|
className: 'btn-link',
|
|
},
|
|
save: {
|
|
label: '[[global:save]]',
|
|
className: 'btn-primary',
|
|
callback: function () {
|
|
const expiryEl = modal.get(0).querySelector('#expiry');
|
|
let expiry = expiryEl.value;
|
|
|
|
// No expiry set
|
|
if (expiry === '') {
|
|
return onSuccess();
|
|
}
|
|
|
|
// Expiration date set
|
|
expiry = new Date(expiry);
|
|
|
|
if (expiry && expiry.getTime() > Date.now()) {
|
|
body.expiry = expiry.getTime();
|
|
onSuccess();
|
|
} else {
|
|
alerts.error('[[error:invalid-date]]');
|
|
}
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
};
|
|
|
|
ThreadTools.setLockedState = function (data) {
|
|
const threadEl = components.get('topic');
|
|
if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) {
|
|
return;
|
|
}
|
|
|
|
const isLocked = data.isLocked && !ajaxify.data.privileges.isAdminOrMod;
|
|
|
|
components.get('topic/lock').toggleClass('hidden', data.isLocked).parent().attr('hidden', data.isLocked ? '' : null);
|
|
components.get('topic/unlock').toggleClass('hidden', !data.isLocked).parent().attr('hidden', !data.isLocked ? '' : null);
|
|
|
|
const hideReply = !!((data.isLocked || ajaxify.data.deleted) && !ajaxify.data.privileges.isAdminOrMod);
|
|
|
|
components.get('topic/reply/container').toggleClass('hidden', hideReply);
|
|
components.get('topic/reply/locked').toggleClass('hidden', ajaxify.data.privileges.isAdminOrMod || !data.isLocked || ajaxify.data.deleted);
|
|
|
|
threadEl.find('[component="post"]:not(.deleted) [component="post/reply"], [component="post"]:not(.deleted) [component="post/quote"]').toggleClass('hidden', hideReply);
|
|
threadEl.find('[component="post/edit"], [component="post/delete"]').toggleClass('hidden', isLocked);
|
|
|
|
threadEl.find('[component="post"][data-uid="' + app.user.uid + '"].deleted [component="post/tools"]').toggleClass('hidden', isLocked);
|
|
|
|
$('[component="topic/labels"] [component="topic/locked"]').toggleClass('hidden', !data.isLocked);
|
|
$('[component="post/tools"] .dropdown-menu').html('');
|
|
ajaxify.data.locked = data.isLocked;
|
|
|
|
posts.addTopicEvents(data.events);
|
|
};
|
|
|
|
ThreadTools.setDeleteState = function (data) {
|
|
const threadEl = components.get('topic');
|
|
if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) {
|
|
return;
|
|
}
|
|
|
|
components.get('topic/delete').toggleClass('hidden', data.isDelete).parent().attr('hidden', data.isDelete ? '' : null);
|
|
components.get('topic/restore').toggleClass('hidden', !data.isDelete).parent().attr('hidden', !data.isDelete ? '' : null);
|
|
components.get('topic/purge').toggleClass('hidden', !data.isDelete).parent().attr('hidden', !data.isDelete ? '' : null);
|
|
components.get('topic/deleted/message').toggleClass('hidden', !data.isDelete);
|
|
|
|
if (data.isDelete) {
|
|
app.parseAndTranslate('partials/topic/deleted-message', {
|
|
deleter: data.user,
|
|
deleted: true,
|
|
deletedTimestampISO: utils.toISOString(Date.now()),
|
|
}, function (html) {
|
|
components.get('topic/deleted/message').replaceWith(html);
|
|
html.find('.timeago').timeago();
|
|
});
|
|
}
|
|
const hideReply = data.isDelete && !ajaxify.data.privileges.isAdminOrMod;
|
|
|
|
components.get('topic/reply/container').toggleClass('hidden', hideReply);
|
|
components.get('topic/reply/locked').toggleClass('hidden', ajaxify.data.privileges.isAdminOrMod || !ajaxify.data.locked || data.isDelete);
|
|
threadEl.find('[component="post"]:not(.deleted) [component="post/reply"], [component="post"]:not(.deleted) [component="post/quote"]').toggleClass('hidden', hideReply);
|
|
|
|
threadEl.toggleClass('deleted', data.isDelete);
|
|
ajaxify.data.deleted = data.isDelete ? 1 : 0;
|
|
|
|
posts.addTopicEvents(data.events);
|
|
};
|
|
|
|
|
|
ThreadTools.setPinnedState = function (data) {
|
|
const threadEl = components.get('topic');
|
|
if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) {
|
|
return;
|
|
}
|
|
|
|
components.get('topic/pin').toggleClass('hidden', data.pinned).parent().attr('hidden', data.pinned ? '' : null);
|
|
components.get('topic/unpin').toggleClass('hidden', !data.pinned).parent().attr('hidden', !data.pinned ? '' : null);
|
|
const icon = $('[component="topic/labels"] [component="topic/pinned"]');
|
|
icon.toggleClass('hidden', !data.pinned);
|
|
if (data.pinned) {
|
|
icon.translateAttr('title', (
|
|
data.pinExpiry && data.pinExpiryISO ?
|
|
'[[topic:pinned-with-expiry, ' + data.pinExpiryISO + ']]' :
|
|
'[[topic:pinned]]'
|
|
));
|
|
}
|
|
ajaxify.data.pinned = data.pinned;
|
|
|
|
posts.addTopicEvents(data.events);
|
|
};
|
|
|
|
function setFollowState(state) {
|
|
const titles = {
|
|
follow: '[[topic:watching]]',
|
|
unfollow: '[[topic:not-watching]]',
|
|
ignore: '[[topic:ignoring]]',
|
|
};
|
|
|
|
translator.translate(titles[state], function (translatedTitle) {
|
|
const tooltip = bootstrap.Tooltip.getInstance('[component="topic/watch"]');
|
|
if (tooltip) {
|
|
tooltip.setContent({ '.tooltip-inner': translatedTitle });
|
|
}
|
|
});
|
|
|
|
let menu = components.get('topic/following/menu');
|
|
menu.toggleClass('hidden', state !== 'follow');
|
|
components.get('topic/following/check').toggleClass('fa-check', state === 'follow');
|
|
|
|
menu = components.get('topic/not-following/menu');
|
|
menu.toggleClass('hidden', state !== 'unfollow');
|
|
components.get('topic/not-following/check').toggleClass('fa-check', state === 'unfollow');
|
|
|
|
menu = components.get('topic/ignoring/menu');
|
|
menu.toggleClass('hidden', state !== 'ignore');
|
|
components.get('topic/ignoring/check').toggleClass('fa-check', state === 'ignore');
|
|
}
|
|
|
|
|
|
return ThreadTools;
|
|
});
|