MyDealz Comment Viewer

Zeigt die letzten Kommentare eines Benutzers an

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greasyfork.org/scripts/528796/1803908/MyDealz%20Comment%20Viewer.js.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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);
    }

})();