import $ from 'jquery';
import * as TBApi from '../tbapi.ts';
import * as TBCore from '../tbcore.js';
import * as TBHelpers from '../tbhelpers.js';
import TBListener from '../tblistener.js';
import {Module} from '../tbmodule.jsx';
import * as TBStorage from '../tbstorage.js';
import * as TBui from '../tbui.js';
export default new Module({
name: 'Profile Pro',
id: 'Profile',
enabledByDefault: true,
settings: [
{
id: 'alwaysTbProfile',
type: 'boolean',
default: false,
description: 'Always open toolbox profile overlay on reddit profiles.',
},
{
id: 'profileButtonEnabled',
type: 'boolean',
default: true,
description:
'Show profile button next to usernames in subs you mod. Allows you to quickly open the toolbox profile on that page.',
},
{
id: 'directProfileToLegacy',
type: 'boolean',
default: false,
description: 'Open legacy user overview when clicking on profile links.',
},
{
id: 'subredditColor',
type: 'boolean',
default: () => TBStorage.getSettingAsync('QueueTools', 'subredditColor', false),
hidden: true,
},
{
id: 'onlyshowInhover',
type: 'boolean',
default: () => TBStorage.getSettingAsync('GenSettings', 'onlyshowInhover', true),
hidden: true,
},
],
}, function init ({
alwaysTbProfile,
directProfileToLegacy,
subredditColor,
profileButtonEnabled,
onlyshowInhover,
}) {
const $body = $('body');
let filterModThings = false;
let hideModActions = false;
let cancelSearch = true;
const listingTypes = ['overview', 'submitted', 'comments'];
const self = this;
// When profile links are clicked open them in the legacy view directly
if (directProfileToLegacy) {
$body.on('click', 'a', function (event) {
const userProfileRegex = /(?:\.reddit\.com)?\/(?:user|u)\/[^/]*?\/?$/;
const thisHref = $(this).attr('href');
// If the url matches and we are not on an old style profile already.
if (userProfileRegex.test(thisHref) && !userProfileRegex.test(window.location.href)) {
event.preventDefault();
const lastChar = thisHref.substr(-1);
const newHref = `${thisHref}${lastChar === '/' ? '' : '/'}overview`;
if (event.ctrlKey || event.metaKey) {
window.open(newHref, '_blank');
} else {
window.location.href = newHref;
}
}
});
}
/**
* Will hide or reveal mod actions in the toolbox profile overlay.
* @function hideModActionsThings
* @param {boolean} hide determines if actions should be shown or hidden.
*/
function hideModActionsThings (hide) {
const $things = $('.tb-thing');
if (hide) {
TBCore.forEachChunkedDynamic($things, thing => {
const $thing = $(thing);
const modAction = $thing.find('.tb-moderator').length;
if (modAction) {
$thing.addClass('tb-mod-hidden');
}
}, {framerate: 40});
} else {
TBCore.forEachChunkedDynamic($things, thing => {
const $thing = $(thing);
const modAction = $thing.find('.tb-moderator').length;
if (modAction) {
$thing.removeClass('tb-mod-hidden');
}
}, {framerate: 40});
}
}
/**
* Will hide or reveal items in the profile overlay that can't be modded.
* @function filterModdable
* @param {boolean} hide determines if items should be shown or hidden.
*/
async function filterModdable (hide) {
const $things = $('.tb-thing');
if (hide) {
const mySubs = await TBCore.getModSubs();
TBCore.forEachChunkedDynamic($things, thing => {
const $thing = $(thing);
const subreddit = TBHelpers.cleanSubredditName($thing.attr('data-subreddit'));
if (!mySubs.includes(subreddit)) {
$thing.addClass('tb-mod-filtered');
}
}, {framerate: 40});
} else {
TBCore.forEachChunkedDynamic($things, thing => {
const $thing = $(thing);
$thing.removeClass('tb-mod-filtered');
}, {framerate: 40});
}
}
/**
* Adds items to the toolbox profile overlay siteTable element
* @function addToSiteTable
* @param {array} data array of reddit things to be added in reddit API format
* @param {jqueryObject} $siteTable jquery object representing the siteTable
* @param {string} after reddit thing ID representing the start of the next page. If present will add a "load more" at the end of the siteTable
* @param {callback} callback callback function
* @returns {callback} returned when done
*/
function addToSiteTable (data, $siteTable, after, callback) {
// prepare comment options for TBUi comment creator.
const commentOptions = {
parentLink: true,
contextLink: true,
contextPopup: true,
fullCommentsLink: true,
overviewData: true,
};
const submissionOptions = {};
// Add subredditColor if applicable.
if (subredditColor) {
commentOptions.subredditColor = true;
submissionOptions.subredditColor = true;
}
// Use dynamic chuncking to add all things to the sitetable.
TBCore.forEachChunkedDynamic(data, entry => {
// Comment
if (entry.kind === 't1') {
const $comment = TBui.makeSingleComment(entry, commentOptions);
if (entry.highlight) {
$comment.find('.md').highlight(entry.highlight, '', true);
}
$siteTable.append($comment);
}
// Submission
if (entry.kind === 't3') {
const $submission = TBui.makeSubmissionEntry(entry, submissionOptions);
if (entry.highlight) {
$submission.find('.tb-title, .md').highlight(entry.highlight, '', true);
}
$siteTable.append($submission);
}
}).then(() => {
// More items available on a next page. Add load more element
if (after) {
$siteTable.append(`<div data-after="${after}" class="tb-load-more">load more</div>`);
}
// Fire jsAPI events and apply profile overlay filters where needed.
setTimeout(() => {
TBui.tbRedditEvent($siteTable);
if (filterModThings) {
filterModdable(true);
}
if (hideModActions) {
hideModActionsThings(true);
}
// Done, return callback.
return callback();
}, 1000);
});
}
/**
* Adds the user's trophies to the given sidebar element.
* @function addTrophiesToSidebar
* @param {string} user reddit username
* @param {jqueryObject} $sidebar jquery sidebar element to which the trophies need to be added
*/
function addTrophiesToSidebar (user, $sidebar) {
const inputURL = `/user/${user}/trophies.json`;
TBApi.getJSON(inputURL).then(data => {
if (Object.keys(data).length > 0 && data.constructor === Object) {
TBStorage.purifyObject(data);
const $userTrophies = $(`<div class="tb-user-trophies">
<h3> Trophies </h3>
</div>`).appendTo($sidebar);
data.data.trophies.forEach(trophy => {
let tropyHTML;
const trophyInnerHTML = `
<img class="tb-trophy-icon" src="${trophy.data.icon_40}">
<br>
<span class="tb-trophy-name">${trophy.data.name}</span>
<br>
${
trophy.data.description
? `<span class="tb-trophy-description">${trophy.data.description}</span>`
: ''
}
<br>`;
if (trophy.data.url) {
tropyHTML = `
<div class="tb-trophy-info">
<a href="${trophy.data.url}">
${trophyInnerHTML}
</a>
</div>`;
} else {
tropyHTML = `
<div class="tb-trophy-info">
${trophyInnerHTML}
</div>`;
}
$userTrophies.append(tropyHTML);
});
}
});
}
/**
* Adds the user's moderated subs to the given sidebar element.
* @function addModSubsToSidebar
* @param {string} user reddit username
* @param {jqueryObject} $sidebar jquery sidebar element to which the subreddits need to be added
*/
function addModSubsToSidebar (user, $sidebar) {
const inputURL = `/user/${user}/moderated_subreddits.json`;
TBApi.getJSON(inputURL).then(data => {
if (Object.keys(data).length > 0 && data.constructor === Object) {
TBStorage.purifyObject(data);
const $userModSubs = $(`<div class="tb-user-modsubs">
<h3> ${data.data.length} Moderated subreddits </h3>
</div>`).appendTo($sidebar);
const $moderatedSubListVisible = $('<ul class="tb-user-modsubs-ul"></ul>').appendTo($userModSubs);
const $moderatedSubListExpanded = $('<ul class="tb-user-modsubs-expand-ul"></ul>').appendTo(
$userModSubs,
);
let subCount = 0;
TBCore.forEachChunkedDynamic(data.data, subreddit => {
subCount++;
const subredditName = subreddit.sr;
const iconImage = subreddit.icon_img;
const over18 = subreddit.over_18;
const subscribers = subreddit.subscribers;
const liElement = `<li>
<a href="${
TBCore.link(`/r/${subredditName}`)
}" title="${subscribers} subscribers">/r/${subredditName}</a>
${
over18
? '<span class="tb-nsfw-stamp tb-stamp"><acronym title="Adult content: Not Safe For Work">NSFW</acronym></span>'
: ''
}
${iconImage ? `<img src="${iconImage}" class="tb-subreddit-icon">` : ''}
</li>`;
if (subCount < 10) {
$moderatedSubListVisible.append(liElement);
} else {
$moderatedSubListExpanded.append(liElement);
}
}, {framerate: 40}).then(() => {
if (subCount > 10) {
$moderatedSubListVisible.after(
`<button class="tb-general-button tb-sidebar-loadmod tb-more" data-action="more">${
subCount - 10
} more ...</button>`,
);
$moderatedSubListExpanded.after(
`<button class="tb-general-button tb-sidebar-loadmod tb-less" data-action="less">show ${
subCount - 10
} less</button>`,
);
$body.on('click', '.tb-sidebar-loadmod', function () {
const $this = $(this);
const action = $this.attr('data-action');
if (action === 'more') {
$moderatedSubListExpanded.show();
$sidebar.find('.tb-sidebar-loadmod.tb-less').show();
$this.hide();
} else if (action === 'less') {
$moderatedSubListExpanded.hide();
$sidebar.find('.tb-sidebar-loadmod.tb-more').show();
$this.hide();
}
});
}
addTrophiesToSidebar(user, $sidebar);
});
} else {
addTrophiesToSidebar(user, $sidebar);
}
});
}
/**
* Creates a user sidebar element for the given overlay
* @function makeUserSidebar
* @param {string} user reddit username
* @param {jqueryObject} $overlay jquery overlay element to which the sidebar needs to be added
*/
function makeUserSidebar (user, $overlay) {
const $tabWrapper = $overlay.find('.tb-window-tabs-wrapper');
const inputURL = `/user/${user}/about.json`;
TBApi.getJSON(inputURL).then(data => {
TBStorage.purifyObject(data);
const userThumbnail = data.data.icon_img;
const userCreated = data.data.created_utc;
const verifiedMail = data.data.has_verified_email;
const linkKarma = data.data.link_karma;
const commentKarma = data.data.comment_karma;
const displayName = data.data.subreddit.title;
const publicDescription = data.data.subreddit.public_description;
const createdAt = new Date(userCreated * 1000);
const $sidebar = $(`<div class="tb-profile-sidebar">
${userThumbnail ? `<img src="${userThumbnail}" class="tb-user-thumbnail">` : ''}
<ul class="tb-user-detail-ul">
<li><a href="${TBCore.link(`/user/${user}`)}">/u/${user}</a></li>
${displayName ? `<li>Display name: ${displayName}</li>` : ''}
<li>Link karma: ${linkKarma}</li>
<li>Comment karma: ${commentKarma}</li>
<li class="tb-user-detail-join-date">Joined </li>
<li>${verifiedMail ? 'Verified mail' : 'No verified mail'}</li>
</ul>
${
publicDescription
? `
<div class="tb-user-description">
${publicDescription}
</div>
`
: ''
}
</div>`);
$sidebar.find('.tb-user-detail-join-date').append(TBui.relativeTime(createdAt));
$tabWrapper.after($sidebar);
addModSubsToSidebar(user, $sidebar);
}).catch(error => {
self.error('Error fetching user information:', inputURL, error);
// This CSS uses inline-block and having whitespace screws it
// up, so this HTML has to start immediately after the quote :|
$tabWrapper.after(`<div class="tb-profile-sidebar">
<ul class="tb-user-detail-ul">
<li>No user information found - shadowbanned or deleted?</li>
</ul>
</div>`);
});
}
/**
* Searches a user profile for the given subreddit and/or string
* @function searchProfile
* @param {string} user reddit username
* @param {string} type the listing type that is to be searched
* @param {string} sortMethod the listing type that is to be searched
* @param {jqueryObject} $siteTable jquery siteTable element to which the search results need to be added
* @param {object} options search options
* @param {string} after reddit thing id indicating the next page
* @param {boolean} match indicates if there are previous
* @param {integer} pageCount what page we are on
* @param {callback} callback callback function
* @returns {callback} returns true when results have been found, false when none are found
*/
function searchProfile (user, type, sortMethod, $siteTable, options, after, match, pageCount, callback) {
pageCount++;
let hits = match || false;
const results = [];
const subredditPattern = options.subredditPattern || false;
const searchPattern = options.searchPattern || false;
if (!subredditPattern && !searchPattern) {
return callback(false);
}
// Cancel search if needed.
if (cancelSearch) {
TBui.textFeedback('Search canceled', TBui.FEEDBACK_NEUTRAL);
return callback(hits);
}
const inputURL = `/user/${user}/${type}.json`;
TBApi.getJSON(inputURL, {
raw_json: 1,
after,
sort: sortMethod,
limit: 100,
t: 'all',
}).then(data => {
// Also cancel search here as we really don't need to go over these results.
if (cancelSearch) {
TBui.textFeedback('Search canceled', TBui.FEEDBACK_NEUTRAL);
return callback(hits);
}
TBui.textFeedback(
`Searching profile page ${pageCount} with ${data.data.children.length} items`,
TBui.FEEDBACK_NEUTRAL,
);
TBStorage.purifyObject(data);
data.data.children.forEach(value => {
let hit = false;
let subredditMatch = false;
let patternMatch = false;
if (value.kind === 't1') {
subredditMatch = subredditPattern && subredditPattern.test(value.data.subreddit);
patternMatch = searchPattern && searchPattern.test(value.data.body);
}
if (value.kind === 't3') {
subredditMatch = subredditPattern && subredditPattern.test(value.data.subreddit);
patternMatch = searchPattern
&& (value.data.selftext && searchPattern.test(value.data.selftext)
|| searchPattern.test(value.data.title));
}
if (
subredditMatch && !searchPattern
|| patternMatch && !subredditPattern
|| subredditMatch && patternMatch
) {
hit = true;
}
if (hit) {
if (searchPattern) {
value.highlight = options.searchString;
}
results.push(value);
hits = true;
}
});
if (!data.data.after) {
if (results.length > 0) {
addToSiteTable(results, $siteTable, false, () => callback(hits));
} else {
return callback(hits);
}
} else {
if (results.length > 0) {
addToSiteTable(results, $siteTable, false, () => {
searchProfile(
user,
type,
sortMethod,
$siteTable,
options,
data.data.after,
hits,
pageCount,
found => callback(found),
);
});
} else {
searchProfile(
user,
type,
sortMethod,
$siteTable,
options,
data.data.after,
hits,
pageCount,
found => callback(found),
);
}
}
});
}
/**
* Escapes a string so it is suitable to be inserted in to a regex match
* @function regExpEscape
* @param {string} query reddit username
* @returns {string} string escaped for regex use
*/
function regExpEscape (query) {
return query.trim().replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
}
// Initate user search
$body.on('submit', '.tb-searchuser', function () {
TBui.longLoadSpinner(true);
const $this = $(this);
const $windowContent = $this.closest('.tb-window-content');
const $siteTable = $windowContent.find('.tb-sitetable');
const typeListing = $siteTable.attr('data-listing');
let subredditsearch = $this.find('.tb-subredditsearch').val();
const usersearch = $this.closest('.tb-page-overlay').attr('data-user');
const contentsearch = $this.find('.tb-contentsearch').val();
const useSort = $this.find('.tb-search-sort').is(':checked');
let sortMethod = 'new';
if (useSort) {
sortMethod = $siteTable.attr('data-sort');
}
subredditsearch = subredditsearch.replace(/\/?r\//g, '');
subredditsearch = TBHelpers.htmlEncode(subredditsearch);
$siteTable.removeClass('tb-sitetable-processed');
$siteTable.empty();
const searchOptions = {};
if (subredditsearch) {
searchOptions.subredditPattern = new RegExp(`^${regExpEscape(subredditsearch)}$`, 'i');
}
if (contentsearch) {
searchOptions.searchPattern = TBHelpers.literalRegExp(contentsearch, 'gi');
searchOptions.searchString = contentsearch;
}
cancelSearch = false;
$('.tb-cancel-profile-search').show();
searchProfile(usersearch, typeListing, sortMethod, $siteTable, searchOptions, null, false, 0, results => {
$('.tb-cancel-profile-search').hide();
TBui.textFeedback('Search complete', TBui.FEEDBACK_POSITIVE);
if (results) {
TBui.longLoadSpinner(false);
} else {
TBui.longLoadSpinner(false);
$siteTable.append('<div class="error">no results found</div>');
}
if (cancelSearch) {
$siteTable.append('<div class="error">Search was canceled, results might be incomplete</div>');
}
});
return false;
});
// Cancel search
$body.on('click', '.tb-cancel-profile-search', () => {
TBui.textFeedback('Canceling search', TBui.FEEDBACK_NEUTRAL);
cancelSearch = true;
});
async function populateSearchSuggestion (subreddit) {
const mySubs = await TBCore.getModSubs(false);
if (subreddit && mySubs.includes(subreddit)) {
$body.find('#tb-search-suggest table#tb-search-suggest-list').append(
`<tr data-subreddit="${subreddit}"><td>${subreddit}</td></td></tr>`,
);
}
$(mySubs).each(function () {
if (this !== subreddit) {
$body.find('#tb-search-suggest table#tb-search-suggest-list').append(
`<tr data-subreddit="${this}"><td>${this}</td></td></tr>`,
);
}
});
}
function liveSearch (liveSearchValue) {
$body.find('#tb-search-suggest table#tb-search-suggest-list tr').each(function () {
const $this = $(this);
const subredditName = $this.attr('data-subreddit');
if (subredditName.toUpperCase().indexOf(liveSearchValue.toUpperCase()) < 0) {
$this.hide();
} else {
$this.show();
}
});
}
function initSearchSuggestion (subreddit) {
if (!$body.find('#tb-search-suggest').length) {
$body.append(
'<div id="tb-search-suggest" style="display: none;"><table id="tb-search-suggest-list"></table></div>',
);
populateSearchSuggestion(subreddit);
}
$body.on('focus', '.tb-subredditsearch', function () {
const offset = $(this).offset();
const offsetLeft = offset.left;
const offsetTop = offset.top + 26;
$body.find('#tb-search-suggest').css({
left: `${offsetLeft}px`,
top: `${offsetTop}px`,
});
if (!$body.find('#tb-search-suggest').is(':visible')) {
$body.find('#tb-search-suggest').show();
}
const liveSearchValue = $(this).val();
liveSearch(liveSearchValue);
});
$body.find('.tb-subredditsearch').keyup(function () {
const liveSearchValue = $(this).val();
liveSearch(liveSearchValue);
});
$(document).on('click', event => {
if (
!$(event.target).closest('#tb-search-suggest').length
&& !$(event.target).closest('.tb-subredditsearch').length
) {
$body.find('#tb-search-suggest').hide();
}
});
$body.on('click', '#tb-search-suggest-list tr', function () {
const subSuggestion = $(this).attr('data-subreddit');
$body.find('.tb-subredditsearch:visible').val(subSuggestion);
$body.find('#tb-search-suggest').hide();
});
}
function makeProfile (user, type, options) {
const sort = options.sort || 'new';
const renew = options.renew || false;
const after = options.after || '';
const subreddit = options.subreddit || '';
const content = options.content || '';
const search = options.search || false;
const searchSort = options.searchSort || false;
TBui.longLoadSpinner(true);
let $overlay = $body.find('.tb-profile-overlay');
if (!$overlay.length) {
$overlay = TBui.overlay({
title: `Toolbox profile for /u/${user}`,
tabs: [
{
title: 'overview',
tooltip: 'Overview profile.',
content: `
<div class="tb-profile-options tb-profile-options-overview"></div>
<div class="tb-sitetable tb-sitetable-overview"></div>
`,
footer: '',
},
{
title: 'submitted',
tooltip: 'submitted profile.',
content: `
<div class="tb-profile-options tb-profile-options-submitted"></div>
<div class="tb-sitetable tb-sitetable-submitted"></div>
`,
footer: '',
},
{
title: 'comments',
tooltip: 'comment profile.',
content: `
<div class="tb-profile-options tb-profile-options-comments"></div>
<div class="tb-sitetable tb-sitetable-comments"></div>
`,
footer: '',
},
],
tabOrientation: 'horizontal',
details: {
user,
},
})
.addClass('tb-profile-overlay')
.appendTo('body');
$body.css('overflow', 'hidden');
makeUserSidebar(user, $overlay);
$body.on('click', '.tb-profile-overlay .tb-window-header .close', () => {
// Cancel any ongoing search
cancelSearch = true;
$('.tb-profile-overlay').remove();
$body.css('overflow', 'auto');
filterModThings = false;
});
}
const $siteTable = $overlay.find(`.tb-sitetable-${type}`);
const $options = $overlay.find(`.tb-profile-options-${type}`);
$siteTable.attr({
'data-user': user,
'data-sort': sort,
'data-listing': type,
't': 'all',
});
if ($siteTable.hasClass('tb-sitetable-processed') && !renew && !after) {
TBui.longLoadSpinner(false);
return;
}
// Prevent some issues with people selecting a new sort method while toolbox is still busy.
$options.hide();
// Filter options
let $filterOptions = $options.find('.tb-filter-options');
if (!$filterOptions.length) {
$filterOptions = $(`
<div class="tb-filter-options">
<select class="tb-sort-select tb-general-button" data-type="${type}">
<option value="new">new</option>
<option value="top">top</option>
<option value="controversial">controversial</option>
<option value="hot">hot</option>
</select>
<button class="tb-general-button tb-filter-moddable">${
filterModThings ? 'Show unmoddable' : 'Hide unmoddable'
}</button>
<button name="hideModComments" class="tb-hide-mod-comments tb-general-button">${
hideModActions ? 'Show mod actions' : 'Hide mod actions'
}</a>
</div>`).appendTo($options);
$options.append(`
<form class="tb-searchuser">
search: <input type="text" placeholder="subreddit" class="tb-subredditsearch tb-input tb-search-input"> <input type="text" placeholder="content (optional)" class="tb-contentsearch tb-input tb-search-input">
<label> <input type="checkbox" class="tb-search-sort"> use sort selection </label>
<input type="submit" value=" search " class="tb-action-button">
</form>
${TBui.actionButton('cancel search', 'tb-cancel-profile-search')}
`);
initSearchSuggestion(subreddit);
}
const $sortSelect = $filterOptions.find('.tb-sort-select');
$sortSelect.val(sort);
if (!after) {
$siteTable.addClass('tb-sitetable-processed');
$siteTable.empty();
}
TBui.switchOverlayTab('tb-profile-overlay', type);
if (search) {
const $subreddit = $options.find('.tb-searchuser .tb-subredditsearch');
const $content = $options.find('.tb-searchuser .tb-contentsearch');
const $searchSort = $options.find('.tb-searchuser .tb-search-sort');
const $searchForm = $options.find('.tb-searchuser');
$subreddit.val(subreddit);
$content.val(content);
$searchSort.prop('checked', searchSort);
// Stop spinner to rpevent from duplicating.
TBui.longLoadSpinner(false);
// Show options and submit the search query.
$options.show();
$searchForm.submit();
} else {
const inputURL = `/user/${user}/${type}.json`;
TBApi.getJSON(inputURL, {
raw_json: 1,
after,
sort,
limit: 25,
}).then(data => {
TBStorage.purifyObject(data);
let after = false;
if (data.data.after) {
after = data.data.after;
}
addToSiteTable(data.data.children, $siteTable, after, () => {
TBui.longLoadSpinner(false);
$options.show();
});
}).catch(error => {
self.error('Error fetching profile activity:', inputURL, error);
$('.tb-profile-overlay .tb-window-content').html(`
<h1>No activity found</h1>
<p>Reddit doesn't seem to have anything for this account. Try checking your subreddit's moderation log to find posts and comments from them.</p>
`);
TBui.longLoadSpinner(false);
});
}
}
$body.on('click', '.tb-load-more', function () {
const $this = $(this);
const $siteTable = $this.closest('.tb-sitetable');
const after = $this.attr('data-after');
const user = $siteTable.attr('data-user');
const listing = $siteTable.attr('data-listing');
const sort = $siteTable.attr('data-sort');
makeProfile(user, listing, {sort, renew: false, after});
$this.remove();
});
$body.on('change keydown', '.tb-sort-select', function () {
const $this = $(this);
const newSort = $this.val();
const user = $this.closest('.tb-page-overlay').attr('data-user');
const listing = $this.attr('data-type');
makeProfile(user, listing, {sort: newSort, renew: true});
});
$body.on('click', '.tb-filter-moddable', () => {
const $filterMod = $body.find('.tb-filter-moddable');
if (filterModThings) {
filterModdable(false);
$filterMod.text('Hide unmoddable');
filterModThings = false;
} else {
filterModdable(true);
$filterMod.text('Show unmoddable');
filterModThings = true;
}
});
$body.on('click', '.tb-hide-mod-comments', () => {
const $hideMod = $('.tb-hide-mod-comments');
if (hideModActions) {
hideModActionsThings(false);
$hideMod.text('Hide mod actions');
hideModActions = false;
} else {
hideModActionsThings(true);
$hideMod.text('Show mod actions');
hideModActions = true;
}
});
$body.on('click', '.tb-profile-overlay .tb-window-tabs a', function () {
// Cancel any ongoing profile search first.
cancelSearch = true;
// Start creating specific listing overlay tab
const $this = $(this);
const listing = $this.attr('data-module');
const user = $this.closest('.tb-page-overlay').attr('data-user');
makeProfile(user, listing, {sort: 'new'});
});
window.addEventListener('TBNewPage', event => {
if (event.detail.pageType === 'userProfile' && listingTypes.includes(event.detail.pageDetails.listing)) {
const user = event.detail.pageDetails.user;
const listing = event.detail.pageDetails.listing;
TBui.contextTrigger('tb-user-profile', {
addTrigger: true,
triggerText: 'toolbox profile',
triggerIcon: TBui.icons.profile,
title: `Show toolbox profile for /u/${user}`,
dataAttributes: {
user,
listing,
},
});
if (alwaysTbProfile) {
// Prevent `alwaysTbProfile` from opening if there are hash params for opening a profile.
if (!event.detail.locationHref.includes('#?tbprofile')) {
makeProfile(user, listing, {sort: 'new', renew: false});
}
}
} else {
TBui.contextTrigger('tb-user-profile', {addTrigger: false});
}
});
if (profileButtonEnabled) {
TBListener.on('author', async e => {
const $target = $(e.target);
if (
!$target.closest('.tb-profile-overlay').length
&& (!onlyshowInhover || TBCore.isOldReddit || TBCore.isNewModmail)
) {
const author = e.detail.data.author;
const subreddit = e.detail.data.subreddit.name;
if (author === '[deleted]') {
return;
}
const isMod = await TBCore.isModSub(subreddit);
if (isMod) {
const profileButton =
`<a href="javascript:;" class="tb-user-profile tb-bracket-button" data-listing="overview" data-user="${author}" data-subreddit="${subreddit}" title="view & filter user's profile in toolbox overlay">P</a>`;
requestAnimationFrame(() => {
$target.append(profileButton);
});
}
}
});
TBListener.on('userHovercard', async e => {
const $target = $(e.target);
const subreddit = e.detail.data.subreddit.name;
const author = e.detail.data.user.username;
const isMod = await TBCore.isModSub(subreddit);
if (isMod) {
const profileButton =
`<a href="javascript:;" class="tb-user-profile tb-bracket-button" data-listing="overview" data-user="${author}" data-subreddit="${subreddit}" title="view & filter user's profile in toolbox overlay">Toolbox Profile View</a>`;
$target.append(profileButton);
}
});
}
$body.on('click', '#tb-user-profile, .tb-user-profile', function () {
const $this = $(this);
const user = $this.attr('data-user');
const listing = $this.attr('data-listing');
const subreddit = $this.attr('data-subreddit');
const options = {
sort: 'new',
renew: false,
};
if (subreddit) {
options.subreddit = subreddit;
}
makeProfile(user, listing, options);
});
// Open profile bashed on hash params
window.addEventListener('TBHashParams', event => {
const listing = event.detail.tbprofile;
if (listingTypes.includes(listing)) {
let user;
// If we get a user from the params we go from there.
if (event.detail.user) {
user = event.detail.user;
// If not we check if we are on a profile page.
// We can safely check this object as tbNewPage logic is done before TBHashParams logic.
} else if (TBCore.pageDetails.pageType === 'userProfile') {
user = TBCore.pageDetails.pageDetails.user;
// Finally we simply return if we have no username to work with.
} else {
TBui.textFeedback('No user present in parameters and not on profile page.', TBui.FEEDBACK_NEGATIVE);
return;
}
const options = {
sort: event.detail.sort,
renew: true,
searchSort: event.detail.sort ? true : false,
search: false,
};
if (event.detail.subreddit) {
options.search = true;
options.subreddit = event.detail.subreddit;
}
if (event.detail.content) {
options.search = true;
options.content = event.detail.content;
}
makeProfile(user, listing, options);
}
});
});