Twitter Thread Reader

when we read Twitter|X thread, it's sucks. so I try to generate twitter thread reader in one single page, so i can easily read and share. Anyway, it's easy to read twitter's thread.

// ==UserScript==
// @name         Twitter Thread Reader
// @namespace    https://github.com/WhyWhatHow/
// @homepage     https://github.com/WhyWhatHow/powertoys4browser
// @supportURL   https://github.com/WhyWhatHow/powertoys4browser/issues
// @version      1.8
// @description  when we read Twitter|X thread, it's sucks. so I try to generate twitter thread reader in one single page, so i can easily read and share. Anyway, it's easy to read twitter's thread.
// @author       whywhathow
// @match        https://x.com/
// @match        https://x.com/*/status/*
// @icon         https://abs.twimg.com/favicons/twitter.2.ico
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// @license      MIT
// ==/UserScript==
/***
 * url checking ,if true, means twitter-thread, else ,means twitter.com
 * @param str
 * @returns {boolean}
 */
function checkURL(str) {
    const twitter = "https://x.com/home";
    return twitter === str ? false : true;
}

function getThreadID(url) {
    const arr = url.split("/");
    var threadId = arr[arr.length - 1];
    console.log("ThreadId:-> {}", threadId);
    return threadId;
}

function redirect(url) {
    console.log("准备跳转...");
    console.log(url);
    // window.location.href=url;
    GM_openInTab(url);

}


// actually doing something
function running() {
    // 1. get url and twitter's thread id.
    let currentUrl = window.location.href;
    if (!checkURL(currentUrl)) {
        alert("not home page!!!")
        // not thread page, don't anything.
        return;
    }
    // 2. get thread id
    var threadId = getThreadID(currentUrl);
    // 3. gengerate  thread page.
    // https://twitter-thread.com/api/unroll-thread?id=1643514082681602048
    // send GET Request
    GM_xmlhttpRequest({
        method: "GET",
        url: "https://twitter-thread.com/api/unroll-thread?id=" + threadId,
        headers: {
            "User-Agent": "Mozilla/5.0",
        },
        onload: function (response) {
            //  go to twitter-thread.com to read.
            var redirectUrl = "https://twitter-thread.com/t/" + threadId;
            showMessage("success", "--- enjoy ^_^ ---")
            redirect(redirectUrl);
            // console.log(response.responseText);
        },
        onerror: function (error) {
            showMessage("error", "Load page error, please try again!")
            console.error(error);
        }
    });

}

// Checking if the current focus is in a Twitter input field
function isInTwitterInput() {
    const activeElement = document.activeElement;
    if (activeElement && activeElement.nodeName === "DIV" && activeElement.getAttribute("role") === "textbox") {
        return true;
    } else {
        return false;
    }
}

/**
 * create a notification message component
 * @param type :
 * @returns {HTMLDivElement}
 */
function createMessageElement(type) {
    const messageElement = document.createElement('div');
    messageElement.style.position = 'fixed';
    messageElement.style.top = '20px';
    messageElement.style.right = '20px';
    messageElement.style.zIndex = '9999';
    messageElement.style.width = '350px';
    messageElement.style.maxWidth = '80%';
    messageElement.style.padding = '15px 50px 15px 15px';
    messageElement.style.color = '#fff';
    messageElement.style.fontSize = '14px';
    messageElement.style.lineHeight = '1.5';
    messageElement.style.borderRadius = '4px';
    messageElement.style.boxShadow = '0 2px 12px 0 rgba(0, 0, 0, 0.1)';
    messageElement.style.transition = 'opacity .3s, transform .3s';
    // messageElement.style.opacity = '0';
    messageElement.style.transform = 'translate3d(0, -50%, 0)';
    messageElement.style.pointerEvents = 'none';
    // sett message backgroundColor
    switch (type) {
        case 'success':
            messageElement.style.backgroundColor = '#67C23A';
            break;
        case 'info':
            messageElement.style.backgroundColor = '#909399';
            break;
        case 'error':
            messageElement.style.backgroundColor = '#F56C6C';
            break;
        default:
            break;
    }
    messageElement.style.color = '#fff';
    messageElement.style.fontWeight = 'bold';

    return messageElement;
}


function showMessage(type = "success", message, timeout = 1000) {
    const container = createMessageElement(type);
    const content = document.createElement('p');
    content.style.margin = '0';
    content.innerText = message;
    container.appendChild(content);

    document.body.appendChild(container);
    setTimeout(() => {
        container.style.opacity = '0';
        container.style.transform = 'translate3d(0, -50%, 0)';
        container.style.pointerEvents = 'none';
        container.remove();
    }, timeout);
}

/**
 * create Icon ,choose Emoji (easy to create, don't need to care the fucking unicode),not font-awesome(too heavy and not suitable for unicode )
 */
function createIcon(elementType = "span", fontSize = "1.5em", fontWeight = "900", code = "🦄") {
    var icon = document.createElement(elementType);
    icon.style.fontWeight = fontWeight;
    icon.style.fontSize = fontSize; // fa-3x
    icon.innerHTML = code;
    return icon;
}

// icon left click
/**
 * click to call running method
 */
function createIconDiv() {
    // Create the initial div element
    const clickDiv = document.createElement('div');
    clickDiv.style.position = 'fixed';
    clickDiv.style.top = '30%';
    clickDiv.style.right = '1%';
    clickDiv.style.width = '30px';
    clickDiv.style.height = '30px';
    clickDiv.style.zIndex = "9999";
    clickDiv.style.cursor = 'pointer';
    //  add icon
    clickDiv.appendChild(createIcon())
    clickDiv.addEventListener('mouseenter', function () {
        clickDiv.style.backgroundColor = 'rgba(255, 68, 0, 0.7)';
        // clickDiv.title = 'Hello, World!';
        // console.log("mouseenter--------------------");
    });
    clickDiv.addEventListener('mouseleave', function () {
        clickDiv.style.backgroundColor = 'rgba(255, 68, 0, 0.5)';
        // clickDiv.title = '';
        // console.log("mouseleave ----------------------")
    });
    // left click
    clickDiv.addEventListener('click', function () {
        // console.log("-----------left click-----------------")
        running();
    });

    document.body.appendChild(clickDiv);
}

// main method
(function () {
    'use strict';
    GM_registerMenuCommand('read thread in another page', running, 'f');
    createIconDiv();
    document.addEventListener('keydown', function (event) {
        if (event.ctrlKey && event.key === 'f') {
            return;
        }
        // If the f key is pressed and the event target is not an input field
        else if (event.key === 'f' && !isInTwitterInput()) {
            console.log('f------------------------------------f');
            // alert("just wait")
            showMessage("info", "Loading Page...", 1000)
            running()
        }
    }, true);
})();