Greasy Fork is available in English.

Xbox Wishlist Sale Items Only

Display sale items with sorting options (discount, price, rating) and toggle sort order

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Xbox Wishlist Sale Items Only
// @namespace    http://tampermonkey.net/
// @version      4
// @description  Display sale items with sorting options (discount, price, rating) and toggle sort order
// @match        https://www.xbox.com/*/wishlist
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    let currentSortKey = 'discountPercentage';
    let sortOrder = 'desc';

    const getWishlistData = () => {
        try {
            const script = [...document.scripts].find(s => s.innerText.includes('window.__PRELOADED_STATE__'));
            const dataMatch = script?.innerText.match(/window\.__PRELOADED_STATE__\s*=\s*(\{.*\})\s*;/);
            return dataMatch ? Object.values(JSON.parse(dataMatch[1]).core2?.products?.productSummaries || {}) : [];
        } catch {
            console.error("Error retrieving wishlist data");
            return [];
        }
    };

    const injectStyles = () => {
        const css = `
            body {
                background-color: #121212;
                color: #E0E0E0;
                font-family: 'Roboto', sans-serif;
            }

            .wishlist-container {
                margin: 20px auto;
                padding: 20px;
                background: #1E1E1E;
                border: 2px solid #333;
                border-radius: 12px;
                box-shadow: 0 4px 10px rgba(0, 0, 0, 0.6);
                max-width: 1200px;
            }

            .wishlist-title {
                text-align: center;
                font-size: 24px;
                font-weight: bold;
                margin-bottom: 20px;
                color: #E0E0E0;
                text-transform: uppercase;
                border-bottom: 2px solid #333;
                padding-bottom: 10px;
            }

            .wishlist-grid {
                display: grid;
                gap: 15px;
                grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
                padding: 0;
            }

.custom-card {
    position: relative;
    background: #2A2A2A;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
    border: 1px solid transparent;
    transition: transform 0.3s, box-shadow 0.3s;
}

.custom-card .image-wrapper {
    position: relative;
    width: 100%;
    padding-top: 150%; /* Maintains 720x1080 aspect ratio (height is 1.5 times the width) */
    overflow: hidden;
    background: #1E1E1E; /* Placeholder background in case image fails to load */
}

.custom-card img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover; /* Ensures the image scales proportionally */
}

.custom-card:hover {
    border: 1px solid white !important;
}



            .info {
                position: absolute;
                bottom: 0;
                left: 0;
                background: rgba(0, 0, 0, 0.8);
                width: 100%;
                display: flex;
                justify-content: space-between;
                padding: 8px 12px;
                box-sizing: border-box;
                font-size: 14px;
                color: #E0E0E0;
            }

            .price {
                color: #4CAF50;
                font-weight: bold;
            }

            .original-price {
                color: #B0BEC5;
                text-decoration: line-through;
                font-size: 12px;
            }

            .discount {
                color: #FF7043;
                font-weight: bold;
            }

            .rating {
                color: #FFD700;
                font-weight: bold;
            }

            .sort-options {
                display: flex;
                justify-content: center;
                margin-bottom: 20px;
                gap: 10px;
            }

            .sort-options select,
            .sort-options button {
                background: #2A2A2A;
                color: #E0E0E0;
                border: 1px solid #444;
                border-radius: 8px;
                padding: 10px 15px;
                font-size: 14px;
                cursor: pointer;
                transition: background 0.3s, box-shadow 0.3s;
            }

            .sort-options select:hover,
            .sort-options button:hover {
                background: #333;
                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
            }
        `;
        const style = document.createElement('style');
        style.textContent = css;
        document.head.appendChild(style);
    };

const displayWishlist = (items) => {
    const grid = document.querySelector('.wishlist-grid');
    grid.innerHTML = '';

    items.filter(({ specificPrices }) => {
        const priceData = specificPrices?.purchaseable?.[0];
        return priceData && priceData.listPrice < priceData.msrp;
    }).sort((a, b) => {
        const getValue = (item, key) => {
            const priceData = item.specificPrices?.purchaseable?.[0] || {};
            if (key === 'discountPercentage') return priceData.discountPercentage || 0;
            if (key === 'listPrice') return priceData.listPrice || 0;
            return item.averageRating || 0;
        };
        return sortOrder === 'asc' ? getValue(a, currentSortKey) - getValue(b, currentSortKey)
                                   : getValue(b, currentSortKey) - getValue(a, currentSortKey);
    }).forEach(({ title = "Unknown", images, specificPrices, averageRating, productId }) => {
        const priceData = specificPrices?.purchaseable?.[0] || {};
        const skuId = priceData.skuId || null;
        const detailUrl = `https://www.xbox.com/en-US/games/store/${title.replace(/\s/g, '-').toLowerCase()}/${productId}/${skuId}`;

        const card = document.createElement('div');
        card.className = 'custom-card';
        card.style.cursor = 'pointer';
        card.onclick = () => window.open(detailUrl, '_blank');
        card.innerHTML = `
            <div class="image-wrapper">
                <img src="${images?.poster?.url || 'https://via.placeholder.com/720x1080'}" alt="${title}">
            </div>
            <div class="info">
                <div>
                    <div class="price">$${priceData.listPrice?.toFixed(2) || 'N/A'}
                        <span class="original-price">${priceData.msrp ? `$${priceData.msrp.toFixed(2)}` : ''}</span>
                    </div>
                    <div class="discount">${priceData.discountPercentage ? `${priceData.discountPercentage.toFixed(0)}% OFF` : ''}</div>
                </div>
                <div class="rating">★ ${averageRating?.toFixed(1) || 'N/A'}</div>
            </div>
        `;
        grid.appendChild(card);
    });
};

    const addSortOptions = (wishlistData) => {
        const container = document.querySelector('.wishlist-container');
        const sortDiv = document.createElement('div');
        sortDiv.className = 'sort-options';
        sortDiv.innerHTML = `
            <select id="sort-select">
                <option value="discountPercentage">Sort by: Discount</option>
                <option value="listPrice">Sort by: Price</option>
                <option value="averageRating">Sort by: Rating</option>
            </select>
            <button id="toggle-order">Sort: Descending</button>
        `;
        container.insertBefore(sortDiv, container.querySelector('.wishlist-grid'));

        document.getElementById('sort-select').addEventListener('change', (e) => {
            currentSortKey = e.target.value;
            displayWishlist(wishlistData);
        });

        document.getElementById('toggle-order').addEventListener('click', () => {
            sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
            document.getElementById('toggle-order').textContent = `Sort: ${sortOrder === 'asc' ? 'Ascending' : 'Descending'}`;
            displayWishlist(wishlistData);
        });
    };

    const init = () => {
        injectStyles();

        const container = document.querySelector('.WishlistPage-module__wishListForm___p6wOx');
        if (!container) return console.error("Wishlist container not found");

        container.classList.add('wishlist-container');
        container.innerHTML = `
            <div class="wishlist-title">My Wishlist</div>
            <div class="wishlist-grid"></div>
        `;

        const wishlistData = getWishlistData();
        addSortOptions(wishlistData);
        displayWishlist(wishlistData);
    };

    window.addEventListener('load', init);
})();