MyDealz Comment Viewer

Zeigt die letzten Kommentare eines Benutzers an

Este script não deve ser instalado diretamente. Este script é uma biblioteca de outros scripts para incluir com o diretório meta // @require https://update.greasyfork.org/scripts/528796/1803908/MyDealz%20Comment%20Viewer.js

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         MyDealz Comment Viewer
// @namespace    http://tampermonkey.net/
// @version      2.6
// @description  Zeigt die letzten Kommentare eines Benutzers an
// @author       MD928835
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Modal-Styles einmalig einfügen
    const style = document.createElement('style');
    style.textContent = `
        #mdcv-overlay {
            position: fixed; inset: 0; background: rgba(0,0,0,0.6);
            z-index: 999999; display: flex; align-items: center; justify-content: center;
        }
        #mdcv-modal {
            background: #f5f5f5; width: 90vw; max-width: 1000px;
            height: 85vh; border-radius: 8px; display: flex; flex-direction: column;
            overflow: hidden; box-shadow: 0 8px 32px rgba(0,0,0,0.3);
        }
        #mdcv-header {
            background: #00a000; height: 56px; display: flex;
            align-items: center; justify-content: center;
            color: white; font-size: 20px; position: relative; flex-shrink: 0;
        }
        #mdcv-header img { height: 36px; position: absolute; left: 16px; }
        #mdcv-close {
            position: absolute; right: 16px; background: none; border: none;
            color: white; font-size: 24px; cursor: pointer; line-height: 1;
        }
        #mdcv-sort { text-align: center; padding: 10px; background: #fff;
            border-bottom: 1px solid #ddd; flex-shrink: 0; }
        #mdcv-body { overflow-y: auto; padding: 16px; flex: 1; }
        .mdcv-card {
            background: white; padding: 1rem; margin: 0.75rem 0;
            border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        #mdcv-mute {
            display: flex; align-items: center; gap: 6px;
            padding: 6px 12px; border-radius: 4px; cursor: pointer;
            background: none; border: 1px solid white; color: white;
            position: absolute; right: 56px; font-size: 13px;
        }
        #mdcv-mute:disabled { opacity: 0.5; cursor: not-allowed; }
    `;
    document.head.appendChild(style);

    window.viewUserComments = async function(username) {

        const fetchDealTitle = async (threadId) => {
            const query = `query getThread($filter: IDFilter!) {
                thread(threadId: $filter) { title }
            }`;
            try {
                const res = await fetch("https://www.mydealz.de/graphql", {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ query, variables: { filter: { eq: threadId } } })
                });
                const result = await res.json();
                return result.data.thread.title || "Titel nicht verfügbar";
            } catch {
                return "Titel nicht verfügbar";
            }
        };

        // Ladeindikator zeigen
        showModal(username, '<p style="text-align:center;padding:2rem">Lade Kommentare…</p>', [], null, null);

        try {
            const response = await fetch(`https://www.mydealz.de/profile/${username}?page=1`);
            if (!response.ok) throw new Error(`HTTP ${response.status}`);
            const html = await response.text();

            const pattern = /href=https:\/\/www\.mydealz\.de\/.*?-(\d+)#(?:comment|reply)-(\d+)/g;
            const ids = [...html.matchAll(pattern)].map(m => ({
                threadId: m[1],
                commentId: m[2],
                url: m[0].replace('href=', '')
            }));

            // User-Metadaten (mutable, isMuted)
            const userQuery = `query userProfile($username: String) {
                user(username: $username) { mutable isMuted }
            }`;
            const userRes = await fetch('/graphql', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ query: userQuery, variables: { username } })
            });
            const userData = await userRes.json();
            const { mutable, isMuted: initialMuted } = userData.data.user;

            // Kommentare parallel laden
            const commentQuery = `query comment($id: ID!) {
                comment(id: $id) { preparedHtmlContent createdAt createdAtTs }
            }`;
            const fetchPromises = ids.map(async ({ threadId, commentId, url }) => {
                try {
                    const [commentRes, title] = await Promise.all([
                        fetch("https://www.mydealz.de/graphql", {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ query: commentQuery, variables: { id: commentId } })
                        }).then(r => r.json()),
                        fetchDealTitle(threadId)
                    ]);
                    const cd = commentRes?.data?.comment;
                    if (!cd) return null;
                    const comment = cd.preparedHtmlContent.replace(/<img[^>]*>/g, '');
                    const date = new Date(cd.createdAtTs * 1000)
                        .toLocaleString('de-DE', {
                            day: '2-digit', month: '2-digit', year: '2-digit',
                            hour: '2-digit', minute: '2-digit'
                        }).replace(',', '');
                    return { title, comment, date, createdAt: cd.createdAt,
                             dealId: threadId, commentId, url };
                } catch {
                    return null;
                }
            });

            const results = (await Promise.all(fetchPromises)).filter(Boolean);
            showModal(username, null, results, mutable, initialMuted);

        } catch (error) {
            console.error("Fehler:", error);
            const body = document.getElementById('mdcv-body');
            if (body) body.innerHTML = `<p style="color:red">Fehler: ${error.message}</p>`;
        }
    };

    function renderCards(results) {
        return results.map(r => `
            <div class="mdcv-card">
                <span title="${r.date}">${r.createdAt}</span>
                <b>${r.title}</b><br>
                ${r.comment}<br>
                <svg width="15" height="16" style="vertical-align:middle">
                    <use xlink:href="/assets/img/ico_632f5.svg#comment"></use>
                </svg>
                <a href="${r.url}" target="_blank">Zum Kommentar</a>
            </div>`).join('');
    }

    function showModal(username, loadingHtml, results, mutable, initialMuted) {
        // Altes Modal entfernen
        document.getElementById('mdcv-overlay')?.remove();

        let isMuted = initialMuted;

        const overlay = document.createElement('div');
        overlay.id = 'mdcv-overlay';

        const modal = document.createElement('div');
        modal.id = 'mdcv-modal';

        // Header
        const header = document.createElement('div');
        header.id = 'mdcv-header';
        header.innerHTML = `
            <img src="https://www.mydealz.de/assets/img/logo/default-light_d4b86.svg" alt="mydealz">
            <a href="https://www.mydealz.de/profile/${username}"
               style="color:white;text-decoration:none" target="_blank">
                ${username}s letzte ${results.length} Kommentare
            </a>`;

        // FIX #3: Mute-Button existiert jetzt tatsächlich im DOM
        if (mutable) {
            const muteBtn = document.createElement('button');
            muteBtn.id = 'mdcv-mute';
            muteBtn.innerHTML = `
                <svg width="15" height="15">
                    <use id="mdcv-mute-icon" xlink:href="/assets/img/ico_632f5.svg#${isMuted ? 'unmute' : 'mute'}"></use>
                </svg>
                <span id="mdcv-mute-text">${username} ${isMuted ? 'nicht mehr stumm schalten' : 'stumm schalten'}</span>`;
            muteBtn.addEventListener('click', async () => {
                muteBtn.disabled = true;
                const endpoint = isMuted
                    ? `/profile/${username}/unmute`
                    : `/profile/${username}/mute`;
                try {
                    const xsrf = document.cookie.split('xsrf_t=')[1]?.split(';')[0]?.replace(/"/g, '');
                    const res = await fetch(endpoint, {
                        method: 'POST',
                        headers: {
                            'X-Request-Type': 'application/vnd.pepper.v1+json',
                            'X-Requested-With': 'XMLHttpRequest',
                            'X-Pepper-Txn': 'user.profile.overview',
                            'X-XSRF-TOKEN': xsrf
                        }
                    });
                    const data = await res.json();
                    if (data.status === 'success') {
                        isMuted = !isMuted;
                        document.getElementById('mdcv-mute-text').textContent =
                            `${username} ${isMuted ? 'nicht mehr stumm schalten' : 'stumm schalten'}`;
                        document.getElementById('mdcv-mute-icon')
                            .setAttribute('xlink:href',
                                `/assets/img/ico_632f5.svg#${isMuted ? 'unmute' : 'mute'}`);
                    }
                } catch (e) {
                    console.error('Mute-Fehler:', e);
                } finally {
                    muteBtn.disabled = false;
                }
            });
            header.appendChild(muteBtn);
        }

        const closeBtn = document.createElement('button');
        closeBtn.id = 'mdcv-close';
        closeBtn.textContent = '×';
        closeBtn.title = 'Schließen';
        closeBtn.addEventListener('click', () => overlay.remove());
        header.appendChild(closeBtn);

        // Sortierung
        const sort = document.createElement('div');
        sort.id = 'mdcv-sort';
        sort.innerHTML = `
            Kommentare sortieren nach
            <label><input type="radio" name="mdcv-sort" checked value="all"> alle chronologisch</label>
            <label><input type="radio" name="mdcv-sort" value="deal"> beitragschronologisch</label>`;

        // Body
        const body = document.createElement('div');
        body.id = 'mdcv-body';
        body.innerHTML = loadingHtml || renderCards(results);

        // Sortierung — kein sessionStorage mehr nötig
        sort.addEventListener('change', (e) => {
            const sorted = [...results];
            if (e.target.value === 'deal') {
                sorted.sort((a, b) =>
                    b.dealId !== a.dealId
                        ? b.dealId - a.dealId
                        : b.commentId - a.commentId);
            } else {
                sorted.sort((a, b) => b.commentId - a.commentId);
            }
            body.innerHTML = renderCards(sorted);
        });

        // Klick außerhalb schließt Modal
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) overlay.remove();
        });

        modal.appendChild(header);
        modal.appendChild(sort);
        modal.appendChild(body);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
    }

})();