mirror of
https://github.com/insin/control-panel-for-twitter.git
synced 2025-06-18 22:55:32 -04:00

- Fixed Discover More Tweets not being hidden when they render before the Discover More heading - Fixed Premium blue check Tweets the focused Tweet is a reply to being hidden - Fixed processing blue checks in the Relevant people box when hiding all other sidebar content
623 lines
20 KiB
JavaScript
623 lines
20 KiB
JavaScript
document.title = chrome.i18n.getMessage(`extensionName`)
|
||
|
||
for (let optionValue of [
|
||
'1000',
|
||
'10000',
|
||
'100000',
|
||
'1000000',
|
||
]) {
|
||
for (let $option of document.querySelectorAll(`option[value="${optionValue}"]`)) {
|
||
$option.textContent = formatFollowerCount(Number(optionValue))
|
||
}
|
||
}
|
||
|
||
for (let optionValue of [
|
||
'badges',
|
||
'comfortable',
|
||
'compact',
|
||
'default',
|
||
'hide',
|
||
'ignore',
|
||
'liked',
|
||
'recent',
|
||
'relevant',
|
||
'separate',
|
||
]) {
|
||
let label = chrome.i18n.getMessage(`option_${optionValue}`)
|
||
for (let $option of document.querySelectorAll(`option[value="${optionValue}"]`)) {
|
||
$option.textContent = label
|
||
}
|
||
}
|
||
|
||
for (let translationId of [
|
||
'addAddMutedWordMenuItemLabel_desktop',
|
||
'addAddMutedWordMenuItemLabel_mobile',
|
||
'alwaysUseLatestTweetsLabel',
|
||
'customCssLabel',
|
||
'debugInfo',
|
||
'debugLabel',
|
||
'debugLogTimelineStatsLabel',
|
||
'debugOptionsLabel',
|
||
'defaultToLatestSearchLabel',
|
||
'disableHomeTimelineInfo',
|
||
'disableHomeTimelineLabel',
|
||
'disableTweetTextFormattingLabel',
|
||
'disabledHomeTimelineRedirectLabel',
|
||
'disabledHomeTimelineRedirectOption_messages',
|
||
'dontUseChirpFontLabel',
|
||
'dropdownMenuFontWeightLabel',
|
||
'enabled',
|
||
'experimentsOptionsLabel',
|
||
'exportConfigLabel',
|
||
'fastBlockLabel',
|
||
'followButtonStyleLabel',
|
||
'followButtonStyleOption_monochrome',
|
||
'followButtonStyleOption_themed',
|
||
'fullWidthContentInfo',
|
||
'fullWidthContentLabel',
|
||
'fullWidthMediaLabel',
|
||
'hideAccountSwitcherLabel',
|
||
'hideAdsNavLabel',
|
||
'hideAllMetricsLabel',
|
||
'hideBookmarkButtonLabel',
|
||
'hideBookmarkMetricsLabel',
|
||
'hideComposeTweetLabel',
|
||
'hideDiscoverSuggestionsLabel',
|
||
'hideExploreNavLabel',
|
||
'hideExploreNavWithSidebarLabel',
|
||
'hideExplorePageContentsLabel',
|
||
'hideFollowingMetricsLabel',
|
||
'hideForYouTimelineLabel',
|
||
'hideGrokLabel',
|
||
'hideGrokTweetsLabel',
|
||
'hideInlinePrompts',
|
||
'hideJobsLabel',
|
||
'hideLikeMetricsLabel',
|
||
'hideLiveBroadcastBarLabel',
|
||
'hideLiveBroadcastsLabel',
|
||
'hideMessagesBottomNavItemLabel',
|
||
'hideMessagesDrawerLabel',
|
||
'hideMetricsLabel',
|
||
'hideMonetizationNavLabel',
|
||
'hideMoreSlideOutMenuItemsOptionsLabel_desktop',
|
||
'hideMoreSlideOutMenuItemsOptionsLabel_mobile',
|
||
'hideProfileHeaderMetricsLabel',
|
||
'hideProfileRetweetsLabel',
|
||
'hideQuoteTweetMetricsLabel',
|
||
'hideReplyMetricsLabel',
|
||
'hideRetweetMetricsLabel',
|
||
'hideSeeNewTweetsLabel',
|
||
'hideShareTweetButtonLabel',
|
||
'hideSidebarContentLabel',
|
||
'hideSpacesNavLabel',
|
||
'hideSubscriptionsLabel',
|
||
'hideSuggestedFollowsLabel',
|
||
'hideTimelineTweetBoxLabel',
|
||
'hideToggleNavigationLabel',
|
||
'hideTweetAnalyticsLinksLabel',
|
||
'hideTwitterBlueRepliesLabel',
|
||
'hideTwitterBlueUpsellsLabel',
|
||
'hideUnavailableQuoteTweetsLabel',
|
||
'hideUnusedUiItemsOptionsLabel',
|
||
'hideVerifiedNotificationsTabLabel',
|
||
'hideViewsLabel',
|
||
'hideWhatsHappeningLabel',
|
||
'hideWhoToFollowEtcLabel',
|
||
'homeTimelineOptionsLabel',
|
||
'listRetweetsLabel',
|
||
'mutableQuoteTweetsLabel',
|
||
'navBaseFontSizeLabel',
|
||
'navDensityLabel',
|
||
'preventNextVideoAutoplayInfo',
|
||
'preventNextVideoAutoplayLabel',
|
||
'quoteTweetsLabel',
|
||
'redirectToTwitterLabel',
|
||
'reduceAlgorithmicContentOptionsLabel',
|
||
'reduceEngagementOptionsLabel',
|
||
'reducedInteractionModeInfo',
|
||
'reducedInteractionModeLabel',
|
||
'replaceLogoLabel',
|
||
'restoreLinkHeadlinesLabel',
|
||
'restoreOtherInteractionLinksLabel',
|
||
'restoreQuoteTweetsLinkLabel',
|
||
'restoreTweetSourceLabel',
|
||
'retweetsLabel',
|
||
'showBlueReplyFollowersCountAmountLabel',
|
||
'showBookmarkButtonUnderFocusedTweetsLabel',
|
||
'showPremiumReplyBusinessLabel',
|
||
'showPremiumReplyFollowedByLabel',
|
||
'showPremiumReplyFollowingLabel',
|
||
'showPremiumReplyGovernmentLabel',
|
||
'showRelevantPeopleLabel',
|
||
'sidebarLabel',
|
||
'sortRepliesLabel',
|
||
'tweakNewLayoutInfo',
|
||
'tweakNewLayoutLabel',
|
||
'tweakQuoteTweetsPageLabel',
|
||
'twitterBlueChecksLabel',
|
||
'twitterBlueChecksOption_replace',
|
||
'uiImprovementsOptionsLabel',
|
||
'uiTweaksOptionsLabel',
|
||
'unblurSensitiveContentLabel',
|
||
'uninvertFollowButtonsLabel',
|
||
'xFixesLabel',
|
||
]) {
|
||
document.getElementById(translationId).textContent = chrome.i18n.getMessage(translationId)
|
||
}
|
||
|
||
for (let translationClass of [
|
||
'hideBookmarksNavLabel',
|
||
'hideCommunitiesNavLabel',
|
||
'hideListsNavLabel',
|
||
'notificationsLabel',
|
||
'saveAndApplyButton',
|
||
]) {
|
||
let translation = chrome.i18n.getMessage(translationClass)
|
||
for (let $el of document.querySelectorAll(`.${translationClass}`)) {
|
||
$el.textContent = translation
|
||
}
|
||
}
|
||
|
||
for (let amount of [1_000, 10_000, 100_000, 1_000_000]) {
|
||
document.querySelector(`option[value="${amount}"]`).textContent = formatFollowerCount(amount)
|
||
}
|
||
|
||
/** @type {boolean} */
|
||
let desktop
|
||
/** @type {boolean} */
|
||
let mobile
|
||
|
||
const $body = document.body
|
||
|
||
if (navigator.userAgent.includes('Safari/') && !/Chrom(e|ium)\//.test(navigator.userAgent)) {
|
||
$body.classList.add('safari', /iP(ad|hone)/.test(navigator.userAgent) ? 'iOS' : 'macOS')
|
||
} else {
|
||
$body.classList.toggle('edge', navigator.userAgent.includes('Edg/'))
|
||
}
|
||
|
||
//#region Default config
|
||
/** @type {import("./types").Config} */
|
||
const defaultConfig = {
|
||
enabled: true,
|
||
debug: false,
|
||
debugLogTimelineStats: false,
|
||
// Default based on the platform if the main script hasn't run on Twitter yet
|
||
version: /(Android|iP(ad|hone))/.test(navigator.userAgent) ? 'mobile' : 'desktop',
|
||
// Shared
|
||
addAddMutedWordMenuItem: true,
|
||
alwaysUseLatestTweets: true,
|
||
defaultToLatestSearch: false,
|
||
disableHomeTimeline: false,
|
||
disabledHomeTimelineRedirect: 'notifications',
|
||
disableTweetTextFormatting: false,
|
||
dontUseChirpFont: false,
|
||
dropdownMenuFontWeight: true,
|
||
fastBlock: true,
|
||
followButtonStyle: 'monochrome',
|
||
hideAdsNav: true,
|
||
hideBookmarkButton: false,
|
||
hideBookmarkMetrics: true,
|
||
hideBookmarksNav: false,
|
||
hideCommunitiesNav: false,
|
||
hideComposeTweet: false,
|
||
hideExplorePageContents: true,
|
||
hideFollowingMetrics: true,
|
||
hideForYouTimeline: true,
|
||
hideGrokNav: true,
|
||
hideGrokTweets: false,
|
||
hideInlinePrompts: true,
|
||
hideJobsNav: true,
|
||
hideLikeMetrics: true,
|
||
hideListsNav: false,
|
||
hideMetrics: false,
|
||
hideMonetizationNav: true,
|
||
hideMoreTweets: true,
|
||
hideNotifications: 'ignore',
|
||
hideProfileRetweets: false,
|
||
hideQuoteTweetMetrics: true,
|
||
hideQuotesFrom: [],
|
||
hideReplyMetrics: true,
|
||
hideRetweetMetrics: true,
|
||
hideSeeNewTweets: false,
|
||
hideShareTweetButton: false,
|
||
hideSubscriptions: true,
|
||
hideTotalTweetsMetrics: true,
|
||
hideTweetAnalyticsLinks: false,
|
||
hideTwitterBlueReplies: false,
|
||
hideTwitterBlueUpsells: true,
|
||
hideUnavailableQuoteTweets: true,
|
||
hideVerifiedNotificationsTab: true,
|
||
hideViews: true,
|
||
hideWhoToFollowEtc: true,
|
||
listRetweets: 'ignore',
|
||
mutableQuoteTweets: true,
|
||
mutedQuotes: [],
|
||
quoteTweets: 'ignore',
|
||
redirectToTwitter: false,
|
||
reducedInteractionMode: false,
|
||
replaceLogo: true,
|
||
restoreLinkHeadlines: true,
|
||
restoreOtherInteractionLinks: false,
|
||
restoreQuoteTweetsLink: true,
|
||
restoreTweetSource: true,
|
||
retweets: 'separate',
|
||
showBlueReplyFollowersCount: false,
|
||
showBlueReplyFollowersCountAmount: '1000000',
|
||
showBookmarkButtonUnderFocusedTweets: true,
|
||
showPremiumReplyBusiness: true,
|
||
showPremiumReplyFollowedBy: true,
|
||
showPremiumReplyFollowing: true,
|
||
showPremiumReplyGovernment: true,
|
||
sortReplies: 'relevant',
|
||
tweakNewLayout: false,
|
||
tweakQuoteTweetsPage: true,
|
||
twitterBlueChecks: 'replace',
|
||
uninvertFollowButtons: true,
|
||
unblurSensitiveContent: false,
|
||
// Experiments
|
||
customCss: '',
|
||
// Desktop only
|
||
fullWidthContent: false,
|
||
fullWidthMedia: true,
|
||
hideAccountSwitcher: false,
|
||
hideExploreNav: true,
|
||
hideExploreNavWithSidebar: true,
|
||
hideLiveBroadcasts: false,
|
||
hideMessagesDrawer: true,
|
||
hideSidebarContent: true,
|
||
hideSpacesNav: false,
|
||
hideSuggestedFollows: false,
|
||
hideTimelineTweetBox: false,
|
||
hideToggleNavigation: false,
|
||
hideWhatsHappening: false,
|
||
navBaseFontSize: true,
|
||
navDensity: 'default',
|
||
showRelevantPeople: false,
|
||
// Mobile only
|
||
hideLiveBroadcastBar: false,
|
||
hideMessagesBottomNavItem: false,
|
||
preventNextVideoAutoplay: true,
|
||
}
|
||
//#endregion
|
||
|
||
//#region Config & variables
|
||
/**
|
||
* Complete configuration for the options page.
|
||
* @type {import("./types").Config}
|
||
*/
|
||
let optionsConfig
|
||
|
||
/**
|
||
* Checkbox group configuration for the version being used (mobile or desktop).
|
||
* @type {Map<string, string[]>}
|
||
*/
|
||
let checkboxGroups
|
||
|
||
// Page elements
|
||
let $experiments = /** @type {HTMLDetailsElement} */ (document.querySelector('details#experiments'))
|
||
let $exportConfig = document.querySelector('#export-config')
|
||
let $form = document.querySelector('form')
|
||
let $hideQuotesFrom = /** @type {HTMLDivElement} */ (document.querySelector('#hideQuotesFrom'))
|
||
let $hideQuotesFromDetails = /** @type {HTMLDetailsElement} */ (document.querySelector('details#hideQuotesFromDetails'))
|
||
let $hideQuotesFromLabel = /** @type {HTMLElement} */ (document.querySelector('#hideQuotesFromLabel'))
|
||
let $mutedQuotes = /** @type {HTMLDivElement} */ (document.querySelector('#mutedQuotes'))
|
||
let $mutedQuotesDetails = /** @type {HTMLDetailsElement} */ (document.querySelector('details#mutedQuotesDetails'))
|
||
let $mutedQuotesLabel = /** @type {HTMLElement} */ (document.querySelector('#mutedQuotesLabel'))
|
||
let $saveCustomCssButton = document.querySelector('button#saveCustomCss')
|
||
let $showBlueReplyFollowersCountLabel = /** @type {HTMLElement} */ (document.querySelector('#showBlueReplyFollowersCountLabel'))
|
||
//#endregion
|
||
|
||
//#region Utility functions
|
||
function exportConfig() {
|
||
let $a = document.createElement('a')
|
||
$a.download = 'control-panel-for-twitter-v4.12.3.config.txt'
|
||
$a.href = URL.createObjectURL(new Blob([
|
||
JSON.stringify(optionsConfig, null, 2)
|
||
], {type: 'text/plain'}))
|
||
$a.click()
|
||
URL.revokeObjectURL($a.href)
|
||
}
|
||
|
||
function formatFollowerCount(num) {
|
||
let numFormat = Intl.NumberFormat(undefined, {notation: 'compact', compactDisplay: num < 1_000_000 ? 'short' : 'long'})
|
||
return numFormat.format(num)
|
||
}
|
||
|
||
/**
|
||
* @param {keyof HTMLElementTagNameMap} tagName
|
||
* @param {({[key: string]: any} | null)?} attributes
|
||
* @param {...any} children
|
||
* @returns {HTMLElement}
|
||
*/
|
||
function h(tagName, attributes, ...children) {
|
||
let $el = document.createElement(tagName)
|
||
|
||
if (attributes) {
|
||
for (let [prop, value] of Object.entries(attributes)) {
|
||
if (prop.startsWith('on') && typeof value == 'function') {
|
||
$el.addEventListener(prop.slice(2).toLowerCase(), value)
|
||
} else {
|
||
$el[prop] = value
|
||
}
|
||
}
|
||
}
|
||
|
||
for (let child of children) {
|
||
if (child == null || child === false) continue
|
||
if (child instanceof Node) {
|
||
$el.appendChild(child)
|
||
} else {
|
||
$el.insertAdjacentText('beforeend', String(child))
|
||
}
|
||
}
|
||
|
||
return $el
|
||
}
|
||
//#endregion
|
||
|
||
//#region Options page functions
|
||
/**
|
||
* Update the options page to match the current config.
|
||
*/
|
||
function applyConfig() {
|
||
mobile = optionsConfig.version == 'mobile'
|
||
desktop = !mobile
|
||
$body.classList.toggle('mobile', mobile)
|
||
$body.classList.toggle('desktop', desktop)
|
||
checkboxGroups = new Map(Object.entries({
|
||
hideAllMetrics: [
|
||
'hideBookmarkMetrics',
|
||
'hideFollowingMetrics',
|
||
'hideLikeMetrics',
|
||
'hideReplyMetrics',
|
||
'hideRetweetMetrics',
|
||
'hideQuoteTweetMetrics',
|
||
'hideTotalTweetsMetrics',
|
||
]
|
||
}))
|
||
updateFormControls()
|
||
updateCheckboxGroups()
|
||
updateDisplay()
|
||
}
|
||
|
||
/**
|
||
* @param {Event} e
|
||
*/
|
||
function onFormChanged(e) {
|
||
if (e.target instanceof HTMLTextAreaElement) return
|
||
|
||
/** @type {Partial<import("./types").Config>} */
|
||
let changedConfig = {}
|
||
|
||
let $el = /** @type {HTMLInputElement} */ (e.target)
|
||
if ($el.type == 'checkbox') {
|
||
if (checkboxGroups.has($el.name)) {
|
||
checkboxGroups.get($el.name).forEach(checkboxName => {
|
||
optionsConfig[checkboxName] = changedConfig[checkboxName] = $el.checked
|
||
updateFormControl($form.elements[checkboxName], $el.checked)
|
||
})
|
||
$el.indeterminate = false
|
||
} else {
|
||
optionsConfig[$el.name] = changedConfig[$el.name] = $el.checked
|
||
// Don't try to redirect the Home timeline to Notifications if both are disabled
|
||
if ($el.name == 'hideNotifications' &&
|
||
$el.checked &&
|
||
optionsConfig.disabledHomeTimelineRedirect == 'notifications') {
|
||
$form.elements['disabledHomeTimelineRedirect'].value = 'messages'
|
||
optionsConfig.disabledHomeTimelineRedirect = 'messages'
|
||
changedConfig.disabledHomeTimelineRedirect = 'messages'
|
||
}
|
||
updateCheckboxGroups()
|
||
}
|
||
} else {
|
||
optionsConfig[$el.name] = changedConfig[$el.name] = $el.value
|
||
}
|
||
|
||
updateDisplay()
|
||
|
||
storeConfigChanges(changedConfig)
|
||
}
|
||
|
||
/**
|
||
* @param {{[key: string]: chrome.storage.StorageChange}} changes
|
||
*/
|
||
function onStorageChanged(changes) {
|
||
let configChanges = Object.fromEntries(
|
||
Object.entries(changes).map(([key, {newValue}]) => [key, newValue])
|
||
)
|
||
Object.assign(optionsConfig, configChanges)
|
||
applyConfig()
|
||
}
|
||
|
||
function saveCustomCss() {
|
||
if (optionsConfig.customCss == $form.elements['customCss'].value) return
|
||
|
||
/** @type {Partial<import("./types").Config>} */
|
||
let changedConfig = {}
|
||
optionsConfig['customCss'] = changedConfig['customCss'] = $form.elements['customCss'].value
|
||
storeConfigChanges(changedConfig)
|
||
}
|
||
|
||
function shouldDisplayHideQuotesFrom() {
|
||
return optionsConfig.mutableQuoteTweets && optionsConfig.hideQuotesFrom.length > 0
|
||
}
|
||
|
||
function shouldDisplayMutedQuotes() {
|
||
return optionsConfig.mutableQuoteTweets && optionsConfig.mutedQuotes.length > 0
|
||
}
|
||
|
||
/**
|
||
* @param {Partial<import("./types").Config>} changes
|
||
*/
|
||
function storeConfigChanges(changes) {
|
||
chrome.storage.onChanged.removeListener(onStorageChanged)
|
||
chrome.storage.local.set(changes, () => {
|
||
chrome.storage.onChanged.addListener(onStorageChanged)
|
||
})
|
||
}
|
||
|
||
function updateCheckboxGroups() {
|
||
for (let [group, checkboxNames] of checkboxGroups.entries()) {
|
||
let checkedCount = checkboxNames.filter(name => optionsConfig[name]).length
|
||
$form.elements[group].checked = checkedCount == checkboxNames.length
|
||
$form.elements[group].indeterminate = checkedCount > 0 && checkedCount < checkboxNames.length;
|
||
}
|
||
}
|
||
|
||
function updateDisplay() {
|
||
$body.classList.toggle('debugging', optionsConfig.debug)
|
||
$body.classList.toggle('chronological', optionsConfig.alwaysUseLatestTweets)
|
||
$body.classList.toggle('disabled', !optionsConfig.enabled)
|
||
$body.classList.toggle('disabledHomeTimeline', optionsConfig.disableHomeTimeline)
|
||
$body.classList.toggle('fullWidthContent', optionsConfig.fullWidthContent)
|
||
$body.classList.toggle('hidingBookmarkButton', optionsConfig.hideBookmarkButton)
|
||
$body.classList.toggle('hidingExploreNav', optionsConfig.hideExploreNav)
|
||
$body.classList.toggle('hidingMetrics', optionsConfig.hideMetrics)
|
||
$body.classList.toggle('hidingNotifications', optionsConfig.hideNotifications == 'hide')
|
||
$body.classList.toggle('hidingQuotesFrom', shouldDisplayHideQuotesFrom())
|
||
$body.classList.toggle('hidingSuggestedFollows', optionsConfig.hideSidebarContent || optionsConfig.hideSuggestedFollows)
|
||
$body.classList.toggle('hidingTwitterBlueReplies', optionsConfig.hideTwitterBlueReplies)
|
||
$body.classList.toggle('mutingQuotes', shouldDisplayMutedQuotes())
|
||
$body.classList.toggle('showingBlueReplyFollowersCount', optionsConfig.showBlueReplyFollowersCount)
|
||
$body.classList.toggle('showingSidebarContent', !optionsConfig.hideSidebarContent)
|
||
$body.classList.toggle('tweakingNewLayout', optionsConfig.tweakNewLayout)
|
||
$body.classList.toggle('uninvertedFollowButtons', optionsConfig.uninvertFollowButtons)
|
||
$showBlueReplyFollowersCountLabel.textContent = chrome.i18n.getMessage(
|
||
'showBlueReplyFollowersCountLabel',
|
||
formatFollowerCount(Number(optionsConfig.showBlueReplyFollowersCountAmount))
|
||
)
|
||
updateHideQuotesFromDisplay()
|
||
updateMutedQuotesDisplay()
|
||
}
|
||
|
||
|
||
function updateHideQuotesFromDisplay() {
|
||
if (!shouldDisplayHideQuotesFrom()) return
|
||
|
||
$hideQuotesFromLabel.textContent = chrome.i18n.getMessage('hideQuotesFromLabel', String(optionsConfig.hideQuotesFrom.length))
|
||
|
||
if (!$hideQuotesFromDetails.open) return
|
||
|
||
while ($hideQuotesFrom.hasChildNodes()) $hideQuotesFrom.firstChild.remove()
|
||
for (let user of optionsConfig.hideQuotesFrom) {
|
||
$hideQuotesFrom.appendChild(
|
||
h('section', null,
|
||
h('label', {className: 'button'},
|
||
h('span', null, `@${user}`),
|
||
h('button', {
|
||
type: 'button',
|
||
onclick() {
|
||
optionsConfig.hideQuotesFrom = optionsConfig.hideQuotesFrom.filter(u => u != user)
|
||
storeConfigChanges({hideQuotesFrom: optionsConfig.hideQuotesFrom})
|
||
updateDisplay()
|
||
}
|
||
}, chrome.i18n.getMessage('unmuteButtonText'))
|
||
)
|
||
)
|
||
)
|
||
}
|
||
}
|
||
|
||
function updateMutedQuotesDisplay() {
|
||
if (!shouldDisplayMutedQuotes()) return
|
||
|
||
$mutedQuotesLabel.textContent = chrome.i18n.getMessage('mutedTweetsLabel', String(optionsConfig.mutedQuotes.length))
|
||
|
||
if (!$mutedQuotesDetails.open) return
|
||
|
||
while ($mutedQuotes.hasChildNodes()) $mutedQuotes.firstChild.remove()
|
||
|
||
optionsConfig.mutedQuotes.forEach(({user, time, text}, index) => {
|
||
$mutedQuotes.appendChild(
|
||
h('section', null,
|
||
h('label', {className: 'button mutedQuote'},
|
||
h('div', null,
|
||
user,
|
||
' – ',
|
||
new Intl.DateTimeFormat([], {dateStyle: 'medium'}).format(new Date(time)),
|
||
text && h('p', {className: 'mb-0'}, text),
|
||
),
|
||
h('button', {
|
||
type: 'button',
|
||
onclick: () => {
|
||
optionsConfig.mutedQuotes = optionsConfig.mutedQuotes.filter((_, i) => i != index)
|
||
chrome.storage.local.set({mutedQuotes: optionsConfig.mutedQuotes})
|
||
updateDisplay()
|
||
},
|
||
}, chrome.i18n.getMessage('unmuteButtonText'))
|
||
)
|
||
)
|
||
)
|
||
})
|
||
}
|
||
|
||
function updateFormControls() {
|
||
Object.keys(optionsConfig)
|
||
.filter(prop => prop in $form.elements)
|
||
.forEach(prop => updateFormControl($form.elements[prop], optionsConfig[prop]))
|
||
}
|
||
|
||
function updateFormControl($control, value) {
|
||
if ($control instanceof RadioNodeList) {
|
||
// If a checkbox displays in multiple sections, update them all
|
||
$control.forEach(input => /** @type {HTMLInputElement} */ (input).checked = value)
|
||
}
|
||
else if ($control.type == 'checkbox') {
|
||
$control.checked = value
|
||
}
|
||
else {
|
||
$control.value = value
|
||
}
|
||
}
|
||
//#endregion
|
||
|
||
//#region Main
|
||
function main() {
|
||
chrome.storage.local.get((/** @type {Partial<import("./types").Config>} */ storedConfig) => {
|
||
// Update deprecated config values
|
||
// @ts-ignore
|
||
if (storedConfig.twitterBlueChecks == 'dim') {
|
||
storedConfig.twitterBlueChecks = 'replace'
|
||
}
|
||
optionsConfig = {...defaultConfig, ...storedConfig}
|
||
|
||
$body.classList.toggle('debug', optionsConfig.debug === true)
|
||
$experiments.open = Boolean(optionsConfig.customCss)
|
||
$exportConfig.addEventListener('click', exportConfig)
|
||
$form.addEventListener('change', onFormChanged)
|
||
$hideQuotesFromDetails.addEventListener('toggle', updateHideQuotesFromDisplay)
|
||
$mutedQuotesDetails.addEventListener('toggle', updateMutedQuotesDisplay)
|
||
$saveCustomCssButton.addEventListener('click', saveCustomCss)
|
||
chrome.storage.onChanged.addListener(onStorageChanged)
|
||
|
||
if (!optionsConfig.debug) {
|
||
let $version = document.querySelector('#version')
|
||
let $debugCountdown = document.querySelector('#debugCountdown')
|
||
let debugCountdown = 5
|
||
|
||
function onClick(e) {
|
||
if (e.target === $version || $version.contains(/** @type {Node} */ (e.target))) {
|
||
debugCountdown--
|
||
} else {
|
||
debugCountdown = 5
|
||
}
|
||
|
||
if (debugCountdown == 0) {
|
||
$body.classList.add('debug')
|
||
$debugCountdown.textContent = ''
|
||
$form.removeEventListener('click', onClick)
|
||
}
|
||
else if (debugCountdown <= 3) {
|
||
$debugCountdown.textContent = ` (${debugCountdown})`
|
||
}
|
||
}
|
||
|
||
$form.addEventListener('click', onClick)
|
||
}
|
||
|
||
applyConfig()
|
||
})
|
||
}
|
||
|
||
main()
|
||
//#endregion
|