✨ Simple Pull to Refresh

🌟 Minimal Premium: Простое и стабильное обновление страницы потягиванием для браузера Via

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

Advertisement:

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

Advertisement:

// ==UserScript==
// @name         ✨ Simple Pull to Refresh
// @namespace    r1kov
// @version      0.3-fix
// @description  🌟 Minimal Premium: Простое и стабильное обновление страницы потягиванием для браузера Via
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/UserScript==
(function() {
    'use strict';

    const config = {
        threshold: 100, // Минимальное расстояние для срабатывания обновления
        elementId: 'simple-refresh',
        overlayId: 'simple-overlay',
        colors: {
            pull: '#333',
            release: '#ffcc00',
            loading: '#007aff'
        },
        messages: {
            pull: 'потяните вниз',
            release: 'отпустите',
            loading: 'обновление'
        }
    };

    let state = {
        startY: 0,
        currentY: 0,
        isActive: false
    };

    function injectStyles() {
        const style = document.createElement('style');
        style.textContent = `
            #${config.elementId} {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                height: 64px;
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 999999;
                transform: translateY(-100%);
                transition: transform 0.3s ease-out;
                pointer-events: none;
                font-family: -apple-system, system-ui, sans-serif;
            }
            #${config.elementId} .pill {
                display: flex;
                align-items: center;
                gap: 8px;
                padding: 12px 20px;
                background: ${config.colors.pull};
                color: white;
                border-radius: 26px;
                font-size: 14px;
                font-weight: 500;
                transition: background 0.3s ease-out;
            }
            #${config.elementId} .icon {
                font-size: 18px;
            }
            #${config.overlayId} {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(255, 255, 255, 0);
                backdrop-filter: blur(0px);
                -webkit-backdrop-filter: blur(0px);
                z-index: 999998;
                pointer-events: none;
                transition: all 0.3s ease-out;
            }
            @keyframes rotate {
                to { transform: rotate(360deg); }
            }
        `;
        document.head.appendChild(style);
    }

    function createElements() {
        if (document.getElementById(config.elementId) || document.getElementById(config.overlayId)) {
            return {
                overlay: document.getElementById(config.overlayId),
                refreshEl: document.getElementById(config.elementId)
            };
        }

        const overlay = document.createElement('div');
        overlay.id = config.overlayId;
        document.body.appendChild(overlay);

        const refreshEl = document.createElement('div');
        refreshEl.id = config.elementId;
        refreshEl.innerHTML = `
            <div class="pill">
                <span class="icon">↓</span>
                <span class="text">${config.messages.pull}</span>
            </div>
        `;
        document.body.appendChild(refreshEl);

        return { overlay, refreshEl };
    }

    function updateUI(distance) {
        const { overlay, refreshEl } = createElements();
        const translateY = Math.min(distance * 0.5, 64);
        refreshEl.style.transform = `translateY(${translateY - 64}px)`;
        overlay.style.background = `rgba(255, 255, 255, ${distance / config.threshold * 0.1})`;
        overlay.style.backdropFilter = `blur(${distance / config.threshold * 8}px)`;
        overlay.style.webkitBackdropFilter = `blur(${distance / config.threshold * 8}px)`;

        if (distance > config.threshold) {
            refreshEl.querySelector('.pill').style.background = config.colors.release;
            refreshEl.querySelector('.text').textContent = config.messages.release;
        } else {
            refreshEl.querySelector('.pill').style.background = config.colors.pull;
            refreshEl.querySelector('.text').textContent = config.messages.pull;
        }
    }

    function reset() {
        const { overlay, refreshEl } = createElements();
        refreshEl.style.transform = 'translateY(-100%)';
        overlay.style.background = 'rgba(255, 255, 255, 0)';
        overlay.style.backdropFilter = 'blur(0px)';
        overlay.style.webkitBackdropFilter = 'blur(0px)';

        setTimeout(() => {
            if (refreshEl && refreshEl.parentNode) refreshEl.remove();
            if (overlay && overlay.parentNode) overlay.remove();
        }, 300);
    }

    function refresh() {
        const { refreshEl } = createElements();
        refreshEl.querySelector('.pill').style.background = config.colors.loading;
        refreshEl.querySelector('.icon').textContent = '↻';
        refreshEl.querySelector('.text').textContent = config.messages.loading;

        setTimeout(() => {
            window.location.reload();
        }, 300);
    }

    function handleTouchStart(e) {
        if (window.scrollY === 0 && !e.target.matches('input, textarea, select')) {
            state.startY = e.touches[0].pageY;
            state.currentY = state.startY;
            state.isActive = true;
        }
    }

    function handleTouchMove(e) {
        if (!state.isActive) return;
        state.currentY = e.touches[0].pageY;
        const distance = state.currentY - state.startY;

        if (distance > 0 && window.scrollY === 0) {
            e.preventDefault();
            requestAnimationFrame(() => updateUI(distance));
        }
    }

    function handleTouchEnd() {
        if (!state.isActive) return;
        const distance = state.currentY - state.startY;

        if (distance > config.threshold) {
            refresh();
        } else {
            reset();
        }

        state.isActive = false;
    }

    injectStyles();

    document.addEventListener('touchstart', handleTouchStart, { passive: true });
    document.addEventListener('touchmove', handleTouchMove, { passive: false });
    document.addEventListener('touchend', handleTouchEnd, { passive: true });

    // Очистка элементов при переходе на другую страницу
    window.addEventListener('beforeunload', () => {
        const refreshEl = document.getElementById(config.elementId);
        const overlay = document.getElementById(config.overlayId);

        if (refreshEl && refreshEl.parentNode) refreshEl.remove();
        if (overlay && overlay.parentNode) overlay.remove();
    });
})();