teratail NG
by
Lhankor_Mhy
2021-07-17 [2021/07/17 16:08:16]
teratailのNG回答者リストをローカルに保存して、質問を見えなくする
-
// ==UserScript==
// @title teratail NG
// @name teratail NG
// @namespace http://let.hatelabo.jp/Lhankor_Mhy/let/jIKH0YzigMAA
// @version 0.1
// @description teratailのNG回答者リストをローカルに保存して、質問を見えなくする
// @author Lhankor_Mhy
// @match https://teratail.com/
// @match https://teratail.com/feed/*
// @match https://teratail.com/tags/*
// @match https://teratail.com/questions/*
// @icon https://www.google.com/s2/favicons?domain=teratail.com
// @license public domain
// @grant none
// ==/UserScript==
(function () {
'use strict';
// CONST
const localStorageKey = '63cc7db5-31e8-7c4f-6dbb-2210c1c056de';
//const headers = new Headers({ 'Authorization': 'Bearer [APIToken]' });
const headers = {};
// pushState hack
const pushState = history.pushState.bind(history);
history.pushState = (...arg) => {
document.querySelectorAll('.boxContentWrap')?.forEach?.(NGEraser);
pushState(...arg);
}
// NGID リストを取得
const NGIDs = new Set(JSON.parse(localStorage.getItem(localStorageKey)));
// 質問ユーザを localStorage から取得して返す、なければ API を叩いて localStorage にセットして返す
const getQuestionUser = async questionId => {
let questionUserName = localStorage.getItem(`${localStorageKey}${questionId}`);
if (!questionUserName) {
questionUserName = (
await (
await fetch(`https://teratail.com/api/v1/questions/${questionId}`, {
headers
})
).json()
).question?.user?.display_name;
questionUserName ??= '退会済みユーザー'; // 退会済みユーザーはAPIが値を返さない
localStorage.setItem(`${localStorageKey}${questionId}`, questionUserName);
}
return questionUserName;
}
// 質問一覧のメインループ
const NGEraser = (element) => {
element.querySelectorAll('.C-questionFeedItem').forEach(async e => {
const txtUpdateGenre = e.querySelector('.txtUpdateGenre')?.textContent;
const questionId = e.querySelector('[data-question-id]').dataset.questionId;
const txtUserName = e.querySelector('.txtUserName .C-textLink')?.textContent?.trim?.();
// ユーザ名がない場合はgetQuestionUser、ある場合で、「分前にコメント」などがあればgetQuestionUser、または「分前に質問」「分前に質問を編集」などでなければgetQuestionUser
const userName = txtUserName && (!txtUpdateGenre || txtUpdateGenre.includes('質問')) ?
txtUserName :
await getQuestionUser(questionId);
// localStorage に退会済みユーザー名が残っていたら表示する
if (e.querySelector('.txtUserName')?.textContent?.trim?.() === '退会済みユーザー' && userName !== '退会済みユーザー') e.querySelector('.txtUserName').textContent += `質問ユーザー:${userName}`;
changeNGView(userName, e);
});
}
// ?えんがちょボタン
const addToggleNG = (element) => {
const toggleNG = document.createElement('span');
const userName = element.querySelector('.c-userName__link').textContent.trim();
toggleNG.dataset.userName = userName;
toggleNG.textContent = '?';
toggleNG.style.cursor = 'pointer';
toggleNG.style.border = "1px solid gray";
toggleNG.style.borderRadius = "3px";
toggleNG.style.background = "lightgray";
toggleNG.addEventListener('click', toggleNGListener(userName));
element.insertAdjacentElement('beforeend', toggleNG)
changeNGView(toggleNG.dataset.userName, toggleNG.parentElement);
}
// NGID表示トグル処理
const changeNGView = (userName, element) => {
if (NGIDs.has(userName)) {
element.style.opacity = "0.1";
} else {
element.style.opacity = "";
}
}
// ?えんがちょボタンのリスナ
const toggleNGListener = userName => event => {
if (NGIDs.has(userName)) {
NGIDs.delete(userName);
} else {
NGIDs.add(userName);
}
changeNGView(userName, event.target.parentElement);
localStorage.setItem(localStorageKey, JSON.stringify([...NGIDs]));
}
// init
document.querySelectorAll('.boxContentWrap')?.forEach?.(NGEraser);
document.querySelectorAll('.p-questioner')?.forEach?.(addToggleNG);
//TODO APIの例外処理 ユーザーページにえんがちょボタンいれる? ユーザー名変更に対応するは無理かな?
})();
-
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。