diff --git a/Extensions/blacklist.css b/Extensions/blacklist.css index 8c153b0b4..9be8ed139 100644 --- a/Extensions/blacklist.css +++ b/Extensions/blacklist.css @@ -260,3 +260,45 @@ background: rgba(var(--black), 0.25); border-color: rgba(var(--black), 0.8); } + +#xkit-bne-custom-panel { + padding: 1em; + font-size: 14px; + line-height: 1.5; + color: rgb(80,80,80); +} + +#xkit-bne-container { + max-height: calc(100vh - 200px); + overflow-y: auto; + padding: 0.5em; +} +#xkit-bne-container > * { + margin-bottom: 0.75em; +} + +#xkit-bne-export-table { + width: 100%; + outline: 1px solid gray; +} +#xkit-bne-export-table th, #xkit-bne-export-table td { + text-align: center; + vertical-align: middle; + padding: 0.4em; + line-height: normal; +} +#xkit-bne-export-table input[type="text"] { + margin: 0; +} +#xkit-bne-export-table thead { + background-color: lightgray; +} +#xkit-bne-export-table tbody tr:not(:last-child) { + border-bottom: 1px solid lightgray; +} +#xkit-bne-word-header { + max-width: 30%; +} +#xkit-bne-tag-header, #xkit-bne-content-header { + width: 15%; +} diff --git a/Extensions/blacklist.js b/Extensions/blacklist.js index 0c2956ba7..201196982 100644 --- a/Extensions/blacklist.js +++ b/Extensions/blacklist.js @@ -1,5 +1,5 @@ //* TITLE Blacklist **// -//* VERSION 3.1.9 **// +//* VERSION 3.2.0 **// //* DESCRIPTION Clean your dash **// //* DETAILS This extension allows you to block posts based on the words you specify. If a post has the text you've written in the post itself or it's tags, it will be replaced by a warning, or won't be shown on your dashboard, depending on your settings. **// //* DEVELOPER new-xkit **// @@ -983,21 +983,20 @@ XKit.extensions.blacklist = new Object({ destroy: function() { this.running = false; - clearTimeout(XKit.extensions.blacklist.check_interval); XKit.post_listener.remove("blacklist"); - setTimeout(function() { - $(".xblacklist-done").each(function() { - $(this).removeClass("xblacklist_blacklisted_post"); - $(this).find(".xblacklist_excuse_container").remove(); - const postContentSel = XKit.css_map.keyToCss('post') || '.post_content'; - $(this).find(postContentSel).html($(this).find(".xblacklist_old_content").html()); - $(this).find(".xkit-shorten-posts-embiggen").css("display", "block"); - XKit.extensions.blacklist.unhide_post($(this)); - }); - $(".xblacklist-done").removeClass("xblacklist-done"); - $(".xblacklist_hidden_post").removeClass("xblacklist_hidden_post"); - $(".xblacklist_blacklisted_post").removeClass("xblacklist_blacklisted_post"); - }, 500); + + $(".xblacklist-done").each(function() { + $(this).removeClass("xblacklist_blacklisted_post"); + $(this).find(".xblacklist_excuse_container").remove(); + const postContentSel = XKit.css_map.keyToCss('post') || '.post_content'; + $(this).find(postContentSel).html($(this).find(".xblacklist_old_content").html()); + $(this).find(".xkit-shorten-posts-embiggen").css("display", "block"); + XKit.extensions.blacklist.unhide_post($(this)); + }); + $(".xblacklist-done").removeClass("xblacklist-done"); + $(".xblacklist_hidden_post").removeClass("xblacklist_hidden_post"); + $(".xblacklist_blacklisted_post").removeClass("xblacklist_blacklisted_post"); + XKit.tools.remove_css("blacklist"); }, @@ -1174,6 +1173,230 @@ XKit.extensions.blacklist = new Object({ $("#xkit-extensions-panel-right").nanoScroller(); $("#xkit-extensions-panel-right").nanoScroller({ scroll: 'top' }); + XKit.extensions.blacklist.nativeExportCpanel(m_div); + }, + + nativeExportCpanel: function(m_div) { + + $('#xkit-bne-custom-panel').remove(); + $(m_div).prepend(` +
+

+ Tumblr now has built-in tag and content filtering that works both in web browsers and on + mobile. You can apply a slimmer layout to natively filtered posts or hide them completely + using the options in Tweaks in + + XKit Rewritten! +

+

+ Export your blacklisted words using this interactive form: +

+ +
+ `); + $('#xkit-bne-button').on('click', () => showNativeExport().catch(showNativeExportError)); + + async function showNativeExport() { + const currentFilteredTags = await apiFetch('/v2/user/filtered_tags') + .then(({ response: { filteredTags } }) => filteredTags); + const currentFilteredContent = await apiFetch('/v2/user/filtered_content') + .then(({ response: { filteredContent } }) => filteredContent); + + const blacklistItemData = (XKit.extensions.blacklist.blacklisted || []) + .map(name => name.trim()) + .filter(Boolean) + .map(name => { + const isTag = name.startsWith('#'); + const initialValue = name.replace(/^#/, '').replace('*', ''); + + const textInput = $(``).get(0); + + const tagCheckbox = $(``).get(0); + const contentCheckbox = $(``).get(0); + tagCheckbox.checked = isTag; + contentCheckbox.checked = !isTag; + + const updateAlreadyFiltered = () => { + const words = getTextInputWords(textInput); + + const isFilteredTag = words.every(word => currentFilteredTags.includes(word)); + tagCheckbox.disabled = isFilteredTag; + if (isFilteredTag) tagCheckbox.checked = true; + + const isFilteredContent = words.every(word => currentFilteredContent.includes(word)); + contentCheckbox.disabled = isFilteredContent; + if (isFilteredContent) contentCheckbox.checked = true; + }; + updateAlreadyFiltered(); + textInput.addEventListener('input', updateAlreadyFiltered); + + return { name, textInput, tagCheckbox, contentCheckbox }; + }); + + if (!blacklistItemData.length) { + XKit.window.show( + 'No Blacklisted Words', + "You don't have any blacklisted words to export!", + 'error', + '
Close
', + ); + return; + } + + const selectAll = () => + blacklistItemData.forEach(({ tagCheckbox, contentCheckbox }) => { + tagCheckbox.checked = true; + contentCheckbox.checked = true; + }); + + const selectNone = () => + blacklistItemData.forEach(({ tagCheckbox, contentCheckbox }) => { + if (!tagCheckbox.disabled) tagCheckbox.checked = false; + if (!contentCheckbox.disabled) contentCheckbox.checked = false; + }); + + const doExport = () => { + const newTagWords = blacklistItemData + .filter(({ tagCheckbox }) => tagCheckbox.checked) + .flatMap(({ textInput }) => getTextInputWords(textInput)) + .filter(word => !currentFilteredTags.includes(word)); + + const newContentWords = blacklistItemData + .filter(({ contentCheckbox }) => contentCheckbox.checked) + .flatMap(({ textInput }) => getTextInputWords(textInput)) + .filter(word => !currentFilteredContent.includes(word)); + + if (newTagWords.length || newContentWords.length) { + Promise.all([ + newTagWords.length && + apiFetch('/v2/user/filtered_tags', { + method: 'POST', + body: { filtered_tags: newTagWords }, + }), + newContentWords.length && + apiFetch('/v2/user/filtered_content', { + method: 'POST', + body: { filtered_content: newContentWords }, + }), + ]).then(() => XKit.window.show( + 'Success', + ` + ${newTagWords.length ? `
Added filtered tags: ${newTagWords.map(word => `#${word}`).join(', ')}
` : ''} + ${newContentWords.length ? `
Added filtered content: ${newContentWords.join(', ')}
` : ''} +
+ You can see your native filtered content in your + + Tumblr account settings. +
+ `, + 'info', + '
Close
', + )).catch(showNativeExportError); + } + }; + + const createRow = (data) => + $('').append(data.map((contents) => $('').append(contents))); + + const rows = blacklistItemData.map(({ name, textInput, tagCheckbox, contentCheckbox }) => + createRow([name, textInput, tagCheckbox, contentCheckbox]) + ); + + XKit.window.show( + 'Tumblr Native Filtering Export', + ` +
+
+ Tumblr's native filtering has two categories of words: filtered tags and + filtered post content. +
+
+ As of 2022: +
+ +
+ Select which of your blacklisted words you wish to add to the native filtering + lists as filtered tags, filtered post content, or both. You can write multiple + variations of a term in a text box, separated by commas, and every variation + will be added. +
+
+ Nothing will be removed from blacklist or from the native filtering lists. +
+
+
Select All
+
Select None
+
+ + + + + + + + +
Blacklist EntryWord(s)/Phrase(s) to ExportFilter as TagFilter as Content
+
+ `, + 'info', + ` +
Export Words
+
Close
+ `, + true + ); + + $('#xkit-bne-export-table-body').append(rows); + $('#xkit-bne-export-select-all').on('click', selectAll); + $('#xkit-bne-export-select-none').on('click', selectNone); + $('#xkit-bne-export-do-export').on('click', doExport); + + centerIt($("#xkit-window")); + } + + function showNativeExportError(e) { + console.error(e); + XKit.window.show( + 'Tumblr Native Filtering Export Error', + `
${e.toString()}
`, + 'error', + '
OK
', + true, + ); + } + + function getTextInputWords(textInput) { + return textInput.value + .split(',') + .map((word) => word.trim().replace(/^#/, '')) + .filter(Boolean); + } + + async function apiFetch(resource, init) { + return XKit.tools.async_add_function( + async ({ resource, init = {}, headerVersion }) => { // eslint-disable-line no-shadow + // add XKit header to all API requests + if (!init.headers) init.headers = {}; + init.headers['X-XKit-Version'] = headerVersion; + + return window.tumblr.apiFetch(resource, init); + }, + { resource, init, headerVersion: XKit.version } + ); + } } });