Fav4Pawchive

Consistent Favorite Button for Pawchive Creator Pages

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Advertisement:

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

Advertisement:

// ==UserScript==
// @name         Fav4Pawchive
// @version      1
// @match        *://pawchive.pw/*
// @grant        GM_addStyle
// @run-at       document-end
// @description  Consistent Favorite Button for Pawchive Creator Pages
// @namespace    c0032
// @license      ohforfuckssakeidontcare
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(`
        .restored-fav-btn {
            display: none !important;
            align-items: center;
            gap: 3px;
            padding: 0;
            margin: 0;
            background: #ff4081;
            color: white;
            border: none;
            border-radius: 3px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
            position: absolute;
            left: 50%;
            top: 0;
            transform: translateX(-50%);
            z-index: 99999;
        }
        .restored-fav-btn.visible { display: inline-flex !important; }
        .restored-fav-btn:hover { background: #f50057; transform: translateX(-50%) scale(1.05); }
        .restored-fav-btn.faved { background: #4caf50; }
        .restored-fav-btn.faved:hover { background: #388e3c; }
    `);

    const state = {
        service: null,
        id: null,
        isFaved: false,
        isFetching: false
    };

    const btn = document.createElement('button');
    btn.className = 'restored-fav-btn';
    document.body.appendChild(btn);

    btn.addEventListener('click', async () => {
        if (!state.service || !state.id || state.isFetching) return;

        const method = state.isFaved ? 'DELETE' : 'POST';
        const url = `/api/v1/favorites/creator/${state.service}/${state.id}`;
        state.isFetching = true;

        try {
            const res = await fetch(url, {
                method,
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' }
            });

            if (res.ok || res.status === 204) {
                state.isFaved = !state.isFaved;
                render();
            } else {
                alert(`Failed (${res.status}). Are you logged in?`);
            }
        } catch (e) {
            console.error('Toggle failed', e);
        } finally {
            state.isFetching = false;
        }
    });

    function render() {
        if (!state.service || !state.id) {
            btn.classList.remove('visible');
            return;
        }
        btn.classList.add('visible');
        btn.textContent = state.isFaved ? '★ Unfavorite' : '☆ Favorite';
        btn.classList.toggle('faved', state.isFaved);
    }

    async function checkStatus() {
        if (!state.service || !state.id) return;

        const currentKey = `${state.service}/${state.id}`;
        state.isFetching = true;

        try {
            const res = await fetch(`/api/v1/account/favorites?type=artist`, { credentials: 'include' });
            if (!res.ok) throw new Error();
            const data = await res.json();

            if (currentKey !== `${state.service}/${state.id}`) return;

            state.isFaved = data.some(item =>
                item.service === state.service &&
                (String(item.id) === state.id || String(item.relation_id) === state.id)
            );
            render();
        } catch (e) {
            console.warn('Check status failed', e);
        } finally {
            state.isFetching = false;
        }
    }

    function handleNavigation() {
        const match = window.location.pathname.match(/^\/([^/]+)\/user\/([^/]+)/);
        const newService = match ? match[1] : null;
        const newId = match ? match[2] : null;

        if (state.service !== newService || state.id !== newId) {
            state.service = newService;
            state.id = newId;
            state.isFaved = false;

            render();
            checkStatus();
        } else if (!document.body.contains(btn)) {
            document.body.appendChild(btn);
        }
    }

    const originalPushState = history.pushState;
    history.pushState = function() {
        originalPushState.apply(this, arguments);
        handleNavigation();
    };

    const originalReplaceState = history.replaceState;
    history.replaceState = function() {
        originalReplaceState.apply(this, arguments);
        handleNavigation();
    };

    window.addEventListener('popstate', handleNavigation);

    handleNavigation();
})();