Vinted Search Saver

Saves Vinted searches for https://github.com/Fuyucch1/Vinted-Notifications/

// ==UserScript==
// @name         Vinted Search Saver
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Saves Vinted searches for https://github.com/Fuyucch1/Vinted-Notifications/
// @author       Quai
// @match        https://*.vinted.de/*
// @match        https://*.vinted.fr/*
// @match        https://*.vinted.be/*
// @match        https://*.vinted.nl/*
// @match        https://*.vinted.it/*
// @match        https://*.vinted.pl/*
// @match        https://*.vinted.cz/*
// @match        https://*.vinted.es/*
// @match        https://*.vinted.se/*
// @match        https://*.vinted.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    
    // ===== USER CONFIGURATION =====
    // Change these settings to match your server configuration
    // If you're running the server locally, leave the default settings
    // If you're running the server on another machine or custom port, update accordingly
    const SERVER_CONFIG = {
        host: 'localhost',  // Change to your server IP or hostname if not running locally
        port: 8000,        // Change if using a different port
        protocol: 'http'   // Change to 'https' if your server uses SSL
    };
    
    // Construct the base URL from configuration
    const SERVER_BASE_URL = `${SERVER_CONFIG.protocol}://${SERVER_CONFIG.host}:${SERVER_CONFIG.port}`;

    const style = document.createElement('style');
    style.textContent = `
        .search-saver-notification {
            position: fixed;
            top: 20px;
            right: 20px;
            background-color: #4CAF50;
            color: white;
            padding: 15px;
            border-radius: 4px;
            z-index: 10000;
            display: none;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        }

        .floating-save-button {
            position: fixed;
            bottom: 30px;
            right: 30px;
            width: 56px;
            height: 56px;
            border-radius: 50%;
            background-color: #09B1BA;
            color: white;
            border: none;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            display: flex;
            align-items: center;
            justify-content: center;
            transition: transform 0.2s, background-color 0.2s;
            z-index: 10000;
        }

        .floating-save-button.disabled {
            background-color: #d9534f;
            cursor: not-allowed;
            opacity: 0.8;
        }

        .floating-save-button.disabled svg {
            opacity: 0.7;
            position: relative;
        }

        .floating-save-button.disabled svg::after {
            content: '';
            position: absolute;
            left: 0;
            top: 50%;
            width: 100%;
            height: 2px;
            background-color: currentColor;
            transform: rotate(-45deg);
        }

        .floating-save-button:hover {
            transform: scale(1.1);
            background-color: #0A9DA5;
        }

        .floating-save-button.disabled:hover {
            background-color: #c9302c;
            transform: scale(1.1);
        }

        .save-modal {
            position: fixed;
            bottom: 100px;
            right: 30px;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            display: none;
            z-index: 10000;
            width: 300px;
        }

        .save-modal input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
            margin-bottom: 10px;
        }

        .save-modal button {
            width: 100%;
            padding: 8px;
            border: none;
            border-radius: 4px;
            background-color: #09B1BA;
            color: white;
            cursor: pointer;
            font-weight: 600;
            transition: background-color 0.2s;
        }

        .save-modal button:hover {
            background-color: #0A9DA5;
        }
    `;
    document.head.appendChild(style);

    const notification = document.createElement('div');
    notification.className = 'search-saver-notification';
    document.body.appendChild(notification);

    function showNotification(message) {
        notification.textContent = message;
        notification.style.display = 'block';
        setTimeout(() => {
            notification.style.display = 'none';
        }, 3000);
    }


    function isValidSearchUrl(url) {
        try {
            const urlObj = new URL(url);
            return urlObj.pathname.includes('/catalog') || urlObj.searchParams.has('search_text');
        } catch {
            return false;
        }
    }

    async function saveSearch(name) {
        const currentUrl = window.location.href;

        if (!isValidSearchUrl(currentUrl)) {
            showNotification('❌ This page is not a valid Vinted search query');
            return;
        }

        try {
            const response = await fetch(`${SERVER_BASE_URL}/add_query`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: `query=${encodeURIComponent(currentUrl)}&name=${encodeURIComponent(name)}`,
            });

            if (response.ok) {
                showNotification('✅ Search successfully saved!');
            } else {
                showNotification('❌ Error saving the search');
            }
        } catch (error) {
            showNotification(`❌ Connection error to backend (${SERVER_BASE_URL})`);
            console.error('Error:', error);
        }
    }

    function createSaveInterface() {

        const floatingButton = document.createElement('button');
        floatingButton.className = 'floating-save-button';

        if (!isValidSearchUrl(window.location.href)) {
            floatingButton.classList.add('disabled');
        }
        floatingButton.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path><line x1="2" y1="4" x2="22" y2="20" stroke-width="2" class="strike-through" style="display: none; stroke: currentColor; stroke-linecap: round;"/></svg>';

        const updateStrikeThrough = () => {
            const strikeLine = floatingButton.querySelector('.strike-through');
            strikeLine.style.display = floatingButton.classList.contains('disabled') ? 'block' : 'none';
        };

        updateStrikeThrough();

        const saveModal = document.createElement('div');
        saveModal.className = 'save-modal';

        const nameInput = document.createElement('input');
        nameInput.type = 'text';
        nameInput.placeholder = 'Search name';

        const saveButton = document.createElement('button');
        saveButton.textContent = 'Save';

        saveModal.appendChild(nameInput);
        saveModal.appendChild(saveButton);

        floatingButton.addEventListener('click', () => {
            const currentUrl = window.location.href;
            if (!isValidSearchUrl(currentUrl)) {
                showNotification('❌ This page is not a valid Vinted search query');
                return;
            }
            saveModal.style.display = saveModal.style.display === 'none' ? 'block' : 'none';
        });

        const observer = new MutationObserver(() => {
            if (!isValidSearchUrl(window.location.href)) {
                floatingButton.classList.add('disabled');
            } else {
                floatingButton.classList.remove('disabled');
            }
            updateStrikeThrough();
        });

        observer.observe(document.body, { childList: true, subtree: true });

        saveButton.addEventListener('click', () => {
            const name = nameInput.value.trim();
            if (!name) {
                showNotification('❌ Please enter a name for the search');
                return;
            }
            saveSearch(name);
            nameInput.value = '';
            saveModal.style.display = 'none';
        });

        document.addEventListener('click', (e) => {
            if (!saveModal.contains(e.target) && !floatingButton.contains(e.target)) {
                saveModal.style.display = 'none';
            }
        });

        document.body.appendChild(floatingButton);
        document.body.appendChild(saveModal);
    }

    createSaveInterface();
})();