63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import React from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import Reactions from './components/Reactions';
|
|
|
|
// --- Подготовка ---
|
|
// Создаем элемент <style>, но пока не вставляем его
|
|
const styleSheet = document.createElement("style");
|
|
styleSheet.innerText = `
|
|
@keyframes reactions-pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
`;
|
|
// Флаг, чтобы убедиться, что стили вставляются только один раз
|
|
let stylesInjected = false;
|
|
|
|
function processCommentContainer(container: HTMLElement) {
|
|
if (container.dataset.reactionsInitialized) return;
|
|
container.dataset.reactionsInitialized = 'true';
|
|
|
|
const issueIdMatch = window.location.pathname.match(/\/issues\/(\d+)/);
|
|
if (!issueIdMatch) return;
|
|
const issueId = parseInt(issueIdMatch[1], 10);
|
|
|
|
const noteElement = container.querySelector<HTMLElement>('div[id^="note-"]');
|
|
if (!noteElement) return;
|
|
const commentId = noteElement.id;
|
|
|
|
const reactionRootEl = document.createElement('div');
|
|
reactionRootEl.style.minHeight = '36px'; // Резервируем место
|
|
container.appendChild(reactionRootEl);
|
|
|
|
const root = ReactDOM.createRoot(reactionRootEl);
|
|
root.render(
|
|
<React.StrictMode>
|
|
<Reactions issueId={issueId} commentId={commentId} />
|
|
</React.StrictMode>
|
|
);
|
|
}
|
|
|
|
const observer = new MutationObserver((mutations) => {
|
|
if (!stylesInjected && document.head) {
|
|
document.head.appendChild(styleSheet);
|
|
stylesInjected = true;
|
|
}
|
|
|
|
for (const mutation of mutations) {
|
|
for (const node of mutation.addedNodes) {
|
|
if (node instanceof HTMLElement) {
|
|
if (node.matches('div.journal.has-notes')) {
|
|
processCommentContainer(node);
|
|
}
|
|
node.querySelectorAll<HTMLElement>('div.journal.has-notes').forEach(processCommentContainer);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
observer.observe(document, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|