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);
};
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 (
<div style={styles.container}>
@ -109,7 +119,7 @@ const Reactions: React.FC<ReactionsProps> = ({ issueId, commentId }) => {
};
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' },
badgeSelected: { backgroundColor: '#e6f2ff', borderColor: '#99ccff' },
emoji: { marginRight: '4px', fontSize: '15px', lineHeight: '1' },
@ -117,6 +127,8 @@ const styles: { [key: string]: React.CSSProperties } = {
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' },
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;

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

Loading…
Cancel
Save