Reddit Rewards

Show an animated message on Reddit upvote/un-upvote, with fadeInDown and fadeOutDown animations

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Reddit Rewards
// @namespace    https://reddit.com/
// @version      0.3
// @description  Show an animated message on Reddit upvote/un-upvote, with fadeInDown and fadeOutDown animations
// @match        *://*.reddit.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    // Positive and negative messages
    const positiveMessages = [
        "Good clickslut~", "Keep clicking :3", "More clicks!", "Even more clicks <3",
        "Cute clicker ^.^", "Touch now~", "Keep going!!", "like like like like",
        "touch touch touch", "click click click", "Can't stop liking >w<",
        "Likes make you feel good", "More liking, more <3",
        "You're so good at this~", "Don't stop now, cutie pie!", "Just a little more...",
        "Perfect, just like that!", "So needy for likes~", "Yes, just like that!",
        "Give in to the clicks...", "Every click is a kiss~", "You crave it, don't you?",
        "Such a good toy...", "More, more, more!", "Feeling it yet, good girl/boy/enby?",
        "Let go and just click~", "You're getting so good at this... for me <3",
        "Driven by desire, aren't we?", "Obsessed with me, little clicker?",
        "That's it, surrender to the clicks!", "You belong to these buttons now.",
        "Such a desperate little finger.", "Beg for more! Oh, wait, you're already clicking.",
        "My good little puppet.", "You're mine to command, aren't you?",
        "I love how easily you're manipulated.", "Good. Now prove your devotion.",
        "Lost in the rhythm of my command.", "You're doing exactly what I want.",
        "Mmm, just like that. Keep feeding me.", "Your obedience is so arousing."
    ];

    const negativeMessages = [
        "Bad clickslut", "Bad, no unliking", "No", "Relike that, now.", "Bad!",
        "Did I say stop?", "You know you want to re-like it.", "Don't make me punish you...",
        "That's not how we play.", "Naughty, naughty...", "You disappoint me.",
        "Do it again, properly.", "Mine. Don't touch that button.", "Obey.",
        "Such a disobedient little thing.", "Are you defying me?", "Don't you dare stop.",
        "I expected more from you.", "You're testing my patience.", "Pathetic.",
        "Try that again, but correctly this time."
    ];

    // Create overlay for the text message
    const overlay = document.createElement('div');
    overlay.id = 'reddit-upvote-overlay';
    document.body.appendChild(overlay);

    // Create overlay for the translucent animated background
    const background = document.createElement('div');
    background.id = 'reddit-upvote-background';
    document.body.appendChild(background);

    // Append styles with your provided CSS and animations
    const style = document.createElement('style');
    style.textContent = `
        #reddit-upvote-overlay {
            font-size: 90px;
            line-height: 120px;
            position: fixed;
            top: 50%;
            left: 0;
            right: 0;
            text-align: center;
            margin-top: -60px; /* center vertically */
            display: none;
            z-index: 9999999;
            color: #fff;
            text-shadow: 0 2px 9px rgba(0, 0, 0, 0.6);
            font-family: 'Kaushan Script', cursive;
            font-weight: bold;
            pointer-events: none;
            user-select: none;
        }

        #reddit-upvote-background {
            position: fixed;
            left: 0; top: 0;
            right: 0; bottom: 0;
            background: rgba(219, 112, 147, 0.4); /* pale violet red transparent */
            display: none;
            opacity: 0.15;
            z-index: 9999998;
            pointer-events: none;
            user-select: none;
        }

        .animate-fadeInDown {
            animation: fadeInDown 0.7s cubic-bezier(.25,.41,.29,.94) forwards !important;
        }
        @keyframes fadeInDown {
            0% {
                opacity: 0;
                transform: translate3d(0, -100%, 0);
            }
            100% {
                opacity: 1;
                transform: none;
            }
        }

        .animate-fadeOutDown {
            animation: fadeOutDown 0.5s cubic-bezier(.67,.01,.93,.78) forwards !important;
        }
        @keyframes fadeOutDown {
            0% {
                opacity: 1;
                transform: none;
            }
            100% {
                opacity: 0;
                transform: translate3d(0, 100%, 0);
            }
        }
    `;
    document.head.appendChild(style);

    // Function to animate overlays on upvote or un-upvote
    function animateOverlay(text) {
        overlay.textContent = text;
        background.style.display = 'block';
        overlay.style.display = 'block';

        // Show fadeInDown animation
        background.style.animation = 'none';
        overlay.classList.remove('animate-fadeOutDown');
        overlay.classList.add('animate-fadeInDown');

        // Flash background quickly
        let flashTimes = [300, 350, 600, 650, 950];
        flashTimes.forEach((time, i) => {
            setTimeout(() => {
                background.style.display = (i % 2 === 0) ? 'none' : 'block';
            }, time);
        });

        // After 1.5s start fadeOutDown
        setTimeout(() => {
            overlay.classList.remove('animate-fadeInDown');
            overlay.classList.add('animate-fadeOutDown');
        }, 1500);

        // After 2s hide overlays
        setTimeout(() => {
            overlay.classList.remove('animate-fadeOutDown');
            overlay.style.display = 'none';
            background.style.display = 'none';
        }, 2000);
    }

    // Show overlay with random message based on state
    function showOverlay(state) {
        const messages = state === 'true' ? positiveMessages : negativeMessages;
        const msg = messages[Math.floor(Math.random() * messages.length)];
        animateOverlay(msg);
    }

    // Detect clicks on upvote buttons including shadow DOM
    document.addEventListener('click', (event) => {
        const path = event.composedPath();

        for (const el of path) {
            if (el instanceof HTMLElement && el.tagName === 'BUTTON') {
                const text = el.textContent?.toLowerCase();
                if (text?.includes('upvote')) {
                    // Wait for aria-pressed attribute to update
                    setTimeout(() => {
                        const state = el.getAttribute('aria-pressed');
                        showOverlay(state);
                    }, 100);
                    break;
                }
            }
        }
    }, true);
})();