placeholder utill we loaded up

master
Sergey Marinkevich 4 months ago
parent db11fff026
commit b4a66ee5c0

@ -80,7 +80,17 @@ const Reactions: React.FC<ReactionsProps> = ({ issueId, commentId }) => {
handleReactionClick(emojiData.emoji); handleReactionClick(emojiData.emoji);
}; };
if (isLoading || !myUserId) return null; if (isLoading) {
return (
<div style={styles.container}>
<div style={{ ...styles.skeleton, width: '50px' }}></div>
<div style={{ ...styles.skeleton, width: '45px' }}></div>
<div style={styles.addButtonSkeleton}></div>
</div>
);
}
if (!myUserId) return null;
return ( return (
<div style={styles.container}> <div style={styles.container}>
@ -109,7 +119,7 @@ const Reactions: React.FC<ReactionsProps> = ({ issueId, commentId }) => {
}; };
const styles: { [key: string]: React.CSSProperties } = { const styles: { [key: string]: React.CSSProperties } = {
container: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '6px', marginTop: '8px', marginBottom: '8px', paddingLeft: '10px', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', fontSize: '13px' }, container: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '6px', marginTop: '8px', marginBottom: '8px', paddingLeft: '10px', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', fontSize: '13px', minHeight: '36px' },
badge: { display: 'flex', alignItems: 'center', padding: '2px 8px', border: '1px solid #dcdcdc', borderRadius: '12px', cursor: 'pointer', transition: 'background-color 0.2s', backgroundColor: '#f7f7f7' }, badge: { display: 'flex', alignItems: 'center', padding: '2px 8px', border: '1px solid #dcdcdc', borderRadius: '12px', cursor: 'pointer', transition: 'background-color 0.2s', backgroundColor: '#f7f7f7' },
badgeSelected: { backgroundColor: '#e6f2ff', borderColor: '#99ccff' }, badgeSelected: { backgroundColor: '#e6f2ff', borderColor: '#99ccff' },
emoji: { marginRight: '4px', fontSize: '15px', lineHeight: '1' }, emoji: { marginRight: '4px', fontSize: '15px', lineHeight: '1' },
@ -117,6 +127,8 @@ const styles: { [key: string]: React.CSSProperties } = {
countSelected: { color: '#005cc5' }, countSelected: { color: '#005cc5' },
addButton: { display: 'flex', alignItems: 'center', padding: '2px 8px', border: '1px dashed #ccc', borderRadius: '12px', backgroundColor: 'transparent', cursor: 'pointer', color: '#555', fontFamily: 'inherit', fontSize: '13px' }, addButton: { display: 'flex', alignItems: 'center', padding: '2px 8px', border: '1px dashed #ccc', borderRadius: '12px', backgroundColor: 'transparent', cursor: 'pointer', color: '#555', fontFamily: 'inherit', fontSize: '13px' },
pickerWrapper: { position: 'absolute', bottom: '100%', left: 0, marginBottom: '10px', zIndex: 1000 }, pickerWrapper: { position: 'absolute', bottom: '100%', left: 0, marginBottom: '10px', zIndex: 1000 },
skeleton: { height: '26px', backgroundColor: '#eef0f2', borderRadius: '12px', animation: 'reactions-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite' },
addButtonSkeleton: { height: '26px', width: '42px', backgroundColor: 'transparent', border: '1px dashed #e0e0e0', borderRadius: '12px' },
}; };
export default Reactions; export default Reactions;

@ -2,40 +2,61 @@ import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import Reactions from './components/Reactions'; import Reactions from './components/Reactions';
console.log('Redmine Reactions Extension Loaded!'); // --- Подготовка ---
// Создаем элемент <style>, но пока не вставляем его
// Функция для извлечения ID задачи из URL const styleSheet = document.createElement("style");
function getIssueId(): number | null { styleSheet.innerText = `
const match = window.location.pathname.match(/\/issues\/(\d+)/); @keyframes reactions-pulse {
return match && match[1] ? parseInt(match[1], 10) : null; 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) => {
const commentContainers = document.querySelectorAll('div.journal.has-notes'); if (!stylesInjected && document.head) {
document.head.appendChild(styleSheet);
const issueId = getIssueId(); stylesInjected = true;
}
if (issueId) {
commentContainers.forEach(container => { for (const mutation of mutations) {
// Находим вложенный div с ID комментария for (const node of mutation.addedNodes) {
const noteElement = container.querySelector<HTMLDivElement>('div[id^="note-"]'); if (node instanceof HTMLElement) {
if (!noteElement) return; if (node.matches('div.journal.has-notes')) {
processCommentContainer(node);
const commentId = noteElement.id; }
node.querySelectorAll<HTMLElement>('div.journal.has-notes').forEach(processCommentContainer);
// Создаем div, в который будем рендерить наш React-компонент }
const reactionRootEl = document.createElement('div'); }
reactionRootEl.className = 'reactions-app-root'; }
});
// Вставляем наш div в конец контейнера, как вы и определили
container.appendChild(reactionRootEl); observer.observe(document, {
childList: true,
// Создаем React-root и рендерим компонент subtree: true,
const root = ReactDOM.createRoot(reactionRootEl); });
root.render(
<React.StrictMode>
<Reactions issueId={issueId} commentId={commentId} />
</React.StrictMode>
);
});
}

Loading…
Cancel
Save