TOKIMEKI メディアスタイル修復

TOKIMEKIの「メディア」スタイルで投稿の本文や引用元をクリックした際に、その投稿の個別ページに移動できるようにします。

// ==UserScript==
// @name         TOKIMEKI メディアスタイル修復
// @namespace    https://bsky.app/profile/neon-ai.art
// @homepage     https://bsky.app/profile/neon-ai.art
// @icon         data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌈</text></svg>
// @version      1.4
// @description  TOKIMEKIの「メディア」スタイルで投稿の本文や引用元をクリックした際に、その投稿の個別ページに移動できるようにします。
// @author       ねおん
// @match        https://tokimeki.blue/*
// @grant        none
// @license      CC BY-NC 4.0
// ==/UserScript==

(function() {
    'use strict';

    // body全体にクリックイベントリスナーを設定して、動的に生成される要素に対応します。
    // これで後から出てくるポップアップにも対応できるんですよ✨
    document.body.addEventListener('click', function(e) {
        const dialog = e.target.closest('dialog.media-content-wrap');
        const selection = window.getSelection();

        // 以下の条件のどれか1つでも当てはまったら、何もしないで処理を終わります。
        // 1. メディアビューのダイアログの外がクリックされた
        // 2. リアクションボタンやリンクなどがクリックされた
        // 3. テキストが選択されている
        if (!dialog ||
            e.target.closest('.timeline-reaction, button, a') ||
            (selection && !selection.isCollapsed)
           ) {
            return;
        }

        // クリックされた要素から一番近い[data-aturi]を持つ親要素を探します。
        // これで、投稿の本文でも引用部分でも、どっちでもいけます!
        const postElement = e.target.closest('[data-aturi]');

        if (postElement) {
            const atUri = postElement.dataset.aturi;

            // atUriがちゃんと投稿のものかチェックします。
            if (atUri && atUri.startsWith('at://') && atUri.includes('/app.bsky.feed.post/')) {
                // atUriをtokimeki.blueのURLに変換します。
                const parts = atUri.replace('at://', '').split('/');
                const did = parts[0];
                const rkey = parts[2];
                const postUrl = `https://tokimeki.blue/profile/${did}/post/${rkey}`;

                // 本来のクリックイベントを止めて、同じタブでURLを書き換えます。
                // これでメディアビューを閉じて、投稿のページに移動します🐾
                e.preventDefault();
                e.stopPropagation();
                window.location.href = postUrl;
            }
        }
    }, true); // キャプチャフェーズでイベントを捕捉して、他のイベントより先に動くようにします。
})();