Last active
May 17, 2022 15:49
-
-
Save mebeim/b50951a0f4bbcaac0c1aa73e6a7cdc66 to your computer and use it in GitHub Desktop.
Fix new "responsive" and unusable design of StackOverflow user profiles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name SO Layout Whack-A-Mole | |
// @description Fix new "responsive" and unusable design of StackOverflow user profiles | |
// @version 0.8.1 | |
// @author Marco Bonelli | |
// @namespace https://mebeim.net | |
// @match https://stackoverflow.com/users/*/* | |
// @downloadURL https://gist.githubusercontent.com/mebeim/b50951a0f4bbcaac0c1aa73e6a7cdc66/raw/so-layout-whack-a-mole.user.js | |
// @updateURL https://gist.githubusercontent.com/mebeim/b50951a0f4bbcaac0c1aa73e6a7cdc66/raw/so-layout-whack-a-mole.user.js | |
// @grant none | |
// ==/UserScript== | |
/** | |
* CHANGELOG | |
* v0.8.1: Minor style adjustments for the "Answers" page | |
* v0.8: Remove patches for "All actions" and Votes pages as they now have a decent layout | |
* v0.7: Update answers page style to include answer date | |
* v0.6: They finally (kind of) fixed the layout of the reputation page, yay! | |
* v0.5: fix answers page link alignment and title length overflowing container | |
* v0.4: fix reputation page number sizes and date alignment | |
* v0.3: fix incorrect styling of answers ans questions panels in summary | |
* page after clicking on a different sort | |
* v0.2: add update url; fix text color of vote counts in answers page | |
*/ | |
(function() { | |
'use strict' | |
function patchBadgeTrophies(el) { | |
if (el.classList.contains('spotAward')) { | |
const container = el.parentElement.parentElement | |
const txt = container.innerText.split('\n') | |
container.innerHTML = `<span style="font-size: 1.3em; margin-right: .3em;">${txt[0]}</span>${txt[1]}` | |
container.style.fontSize = '1.1em' | |
} else if (el.classList.contains('spotAwardLg')) { | |
el.parentElement.parentElement.removeChild(el.parentElement) | |
} | |
} | |
function patchProfilePage() { | |
// Remove "more..." button if the "About" section doesn't need it... SMH | |
document.querySelectorAll('.d-flex > .s-btn.v-hidden').forEach(el => { | |
el.parentElement.parentElement.removeChild(el.parentElement) | |
}) | |
// Remove badge trophies and replace the header with simple text | |
document.querySelectorAll('.svg-spot').forEach(patchBadgeTrophies) | |
const globalStyle = document.createElement('style') | |
globalStyle.innerText = ` | |
.p12 { | |
padding: 3px 5px !important; | |
} | |
.g24 { | |
gap: 15px !important; | |
} | |
` | |
document.head.appendChild(globalStyle) | |
} | |
function patchSummaryPage() { | |
function removeIfEmpty(el) { | |
if (el.querySelector('.s-empty-state') || el.textContent.includes('user has not earned any')) { | |
const p = el.parentElement | |
p.parentElement.removeChild(p) | |
} | |
} | |
// Remove some panels if empty | |
document.querySelectorAll('#user-panel-badges').forEach(removeIfEmpty) | |
document.querySelectorAll('#user-panel-following').forEach(removeIfEmpty) | |
document.querySelectorAll('#user-panel-bookmarks').forEach(removeIfEmpty) | |
document.querySelectorAll('#user-panel-bounties').forEach(removeIfEmpty) | |
document.querySelectorAll('#user-panel-votes').forEach(removeIfEmpty) | |
// Remove badge trophies and replace the header with simple text | |
document.querySelectorAll('.svg-spot').forEach(patchBadgeTrophies) | |
const globalStyle = document.createElement('style') | |
globalStyle.innerText = ` | |
.g24 { | |
gap: 15px !important; | |
} | |
.p12 { | |
padding: 1px 3px !important; | |
} | |
.pr12.pr12 { | |
padding-right: 0 !important; | |
} | |
.bc-black-075 { | |
border: none !important; | |
} | |
.s-badge__votes { | |
min-width: 20px !important; | |
height: 20px !important; | |
margin-right: 0 !important; | |
} | |
.s-badge__rep { | |
height: 20px !important; | |
} | |
.answer-hyperlink, .question-hyperlink { | |
font-size: 1em !important; | |
} | |
.flex--item:has(> .answer-hyperlink) { | |
padding: 0 !important; | |
} | |
.fl-grow1 { | |
padding: 5px !important; | |
} | |
#user-panel-articles { | |
display: none !important; | |
} | |
.js-highlight-box-badges .d-flex.jc-space-between.h100.h100.h100 { | |
height: 75% !important; | |
} | |
#user-panel-votes .p16 { | |
padding: 0 !important; | |
} | |
/* Remove redundant top "Summary" title */ | |
#user-tab-summary > div:first-child { | |
display: none !important; | |
} | |
` | |
document.head.appendChild(globalStyle) | |
} | |
function patchSummaryOrProfilePage() { | |
// This page is "Profile" for our profile, "Summary" if we are not logged in or we are | |
// looking at another user. Consistency FTW! | |
const sel = document.querySelector('.s-navigation--item.is-selected') | |
if (sel.textContent.includes('Profile')) | |
patchProfilePage() | |
else | |
patchSummaryPage() | |
} | |
function patchAnswersPage() { | |
const acceptedDiv = `<div class="goddamn-vote-count goddamn-accepted"></div>`; | |
const notAcceptedDiv = `<div class="goddamn-vote-count"></div>`; | |
function parseVotes(s) { | |
return parseInt(s) + (s.includes('k') ? 'k' : '') | |
} | |
function patchStuff() { | |
document.querySelectorAll('.s-post-summary:not(.patched)').forEach(el => { | |
const container = el.querySelector('.s-post-summary--stats') | |
const accepted = !!(el.querySelector('.has-accepted-answer')) | |
const votes = parseVotes(el.querySelector('.s-post-summary--stats-item-number').textContent) | |
const timeEl = el.querySelector('time.s-user-card--time') | |
const todel = [ | |
'.s-post-summary--stats-item-number', | |
'.s-post-summary--stats-item-unit', | |
'.s-post-summary--stats-item.has-answers.has-accepted-answer', | |
'.s-post-summary--meta-tags .subcommunity-avatar', | |
'.s-post-summary--meta-tags .s-popover', | |
] | |
todel.map(el.querySelector.bind(el)).forEach(x => { | |
if (x) | |
x.parentElement.removeChild(x) | |
}) | |
timeEl.parentElement.removeChild(timeEl) | |
container.innerHTML = (accepted ? acceptedDiv : notAcceptedDiv) + timeEl.textContent.replace(/^answered\s+/, '') | |
container.querySelector('.goddamn-vote-count').textContent = votes | |
el.classList.add('patched') | |
}) | |
} | |
patchStuff() | |
const observer = new MutationObserver(patchStuff) | |
observer.observe(document.body, {childList: true, subtree: true}) | |
const globalStyle = document.createElement('style') | |
globalStyle.innerText = ` | |
.bc-black-100 { | |
border: none !important; | |
} | |
.s-post-summary { | |
padding: 4px 4px 2px 4px !important; | |
} | |
.s-post-summary--content { | |
overflow: hidden; | |
text-overflow: ellipsis; | |
white-space: nowrap; | |
} | |
.s-post-summary--content-title { | |
display: inline-block !important; | |
font-size: 1.15em !important; | |
margin: 4px 0 !important; | |
padding: 0 !important; | |
} | |
.goddamn-vote-count { | |
font-size: 1.1em !important; | |
display: inline-block !important; | |
padding: 1px 4px !important; | |
border-radius: 2px; | |
color: var(--fc-dark); | |
} | |
.goddamn-vote-count.goddamn-accepted { | |
border: 1px solid #62b47d !important; | |
background-color: #62b47d; | |
color: var(--highlight-bg); | |
} | |
.goddamn-vote-count:not(.goddamn-accepted) { | |
border: 1px solid grey !important; | |
border-radius: 2px; | |
} | |
.s-post-summary--meta-tags { | |
display: inline; | |
position: absolute !important; | |
right: 2px; | |
padding-left: 25px; | |
background: linear-gradient(90deg, rgba(255,255,255,0) 0%, var(--theme-background-color) 20px, var(--theme-background-color) 100%); | |
margin-top: -2.2em; | |
} | |
/* Keep only the 2 most relevant tags */ | |
.post-tag:not(:nth-child(-n + 2)) { | |
display: none; | |
} | |
` | |
document.head.appendChild(globalStyle) | |
} | |
function patchTagsPage() { | |
const globalStyle = document.createElement('style') | |
globalStyle.innerText = ` | |
#user-tab-tags { | |
width: 45%; | |
} | |
.p12 { | |
padding: 3px 5px !important; | |
} | |
` | |
document.head.appendChild(globalStyle) | |
} | |
function patchBookmarksPage() { | |
const acceptedDiv = `<div class="s-post-summary--stats-item has-answers has-accepted-answer"></div>`; | |
const notAcceptedDiv = `<div class="s-post-summary--stats-item"></div>`; | |
function parseVotes(s) { | |
return parseInt(s) + (s.includes('k') ? 'k' : '') | |
} | |
function patchStuff() { | |
if (document.querySelector('.s-post-summary.patched')) | |
return | |
document.querySelectorAll('.s-post-summary').forEach(el => { | |
const stats = el.querySelector('.s-post-summary--stats') | |
const accepted = !!(el.querySelector('.s-post-summary--stats-item.has-answers.has-accepted-answer')) | |
const question = el.querySelector('.s-post-summary--content a[href^="/questions"]') | |
const tags = el.querySelector('.s-post-summary--meta-tags') | |
let votes = 0 | |
el.querySelectorAll('.s-post-summary--stats .s-post-summary--stats-item').forEach(el => { | |
if (el.textContent.includes('votes')) | |
votes = parseVotes(el.textContent) | |
}) | |
el.removeChild(el.querySelector('.s-post-summary--content')) | |
el.classList.add('patched') | |
stats.innerHTML = accepted ? acceptedDiv : notAcceptedDiv | |
stats.querySelector('.s-post-summary--stats-item').innerText = votes | |
question.parentElement.removeChild(question) | |
stats.appendChild(question) | |
stats.appendChild(tags) | |
}) | |
} | |
patchStuff() | |
const observer = new MutationObserver(patchStuff) | |
observer.observe(document.body, {childList: true, subtree: true}) | |
const globalStyle = document.createElement('style') | |
globalStyle.innerText = ` | |
.bc-black-100 { | |
border: none !important; | |
} | |
.s-post-summary { | |
padding: 0 !important; | |
border: none !important; | |
} | |
.s-post-summary:hover { | |
background-color: rgba(255, 255, 255, 0.1); | |
} | |
.s-post-summary--stats { | |
font-size: 1.15em !important; | |
margin: 2px 0 !important; | |
} | |
.s-post-summary--stats-item { | |
display: inline !important; | |
padding: 1px 4px !important; | |
margin: 0 4px 0 0 !important; | |
} | |
.s-post-summary--stats-item.has-accepted-answer { | |
color: #0f0f0f !important; | |
} | |
.s-post-summary--stats-item:not(.has-accepted-answer) { | |
color: white; | |
border: 1px solid grey !important; | |
border-radius: 2px; | |
} | |
.s-post-summary--meta-tags { | |
position: absolute !important; | |
right: 0; | |
} | |
/* Keep only the 2 most relevant tags */ | |
.post-tag:not(:nth-child(-n + 2)) { | |
display: none; | |
} | |
` | |
document.head.appendChild(globalStyle) | |
} | |
function patchBadgesPage() { | |
const globalStyle = document.createElement('style') | |
globalStyle.innerText = ` | |
#user-tab-badges .flex--item { | |
margin: 0 5px !important; | |
} | |
` | |
document.head.appendChild(globalStyle) | |
} | |
const patchMap = [ | |
[/tab=profile/ , patchProfilePage ], | |
[/^[^?]+$/ , patchSummaryOrProfilePage], | |
[/tab=summary/ , patchSummaryPage ], | |
[/tab=topactivity/, patchSummaryPage ], | |
[/tab=answers/ , patchAnswersPage ], | |
[/tab=tags/ , patchTagsPage ], | |
[/tab=bookmarks/ , patchBookmarksPage ], | |
[/tab=badges/ , patchBadgesPage ], | |
] | |
for (const [rexp, func] of patchMap) { | |
if (rexp.test(location.href)) | |
func() | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thank you