import React, { useState, useEffect, useMemo } from 'react'; import { useParams, useLocation, Link } from 'react-router-dom'; import api from '../api'; import { useAuth } from '../App'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { useNavigate } from 'react-router-dom'; import SEO from '../components/SEO'; const getYoutubeVideoId = (url: string) => { if (!url) return null; try { if (url.includes('youtu.be/')) { return url.split('youtu.be/')[1].split(/[?#]/)[0]; } else if (url.includes('youtube.com/embed/')) { return url.split('youtube.com/embed/')[1].split(/[?#]/)[0]; } else if (url.includes('youtube.com/shorts/')) { return url.split('youtube.com/shorts/')[1].split(/[?#]/)[0]; } else if (url.includes('v=')) { return url.split('v=')[1].split(/[&?#]/)[0]; } return null; } catch { return null; } }; const getYoutubeEmbedUrl = (url: string) => { if (!url) return ''; const videoId = getYoutubeVideoId(url); return videoId ? `https://www.youtube.com/embed/${videoId}` : url; }; const REPORT_REASONS = [ 'Malware or malicious code', 'Stolen or plagiarised content', 'Misleading description', 'Broken / non-functional', 'Inappropriate content', 'Spam or fake listing', 'Other', ]; const REVIEW_REPORT_REASONS = [ 'Inappropriate language', 'Spam or advertising', 'Hate speech or harassment', 'Off-topic or irrelevant', 'Other', ]; const MAX_REVIEW_LENGTH = 2000; const StarRating = ({ rating, max = 5, onRate }: { rating: number; max?: number; onRate?: (n: number) => void }) => (
{displayText}
{shouldTruncate && ( )}by (e.currentTarget.style.textDecoration = 'underline')} onMouseLeave={e => (e.currentTarget.style.textDecoration = 'none')}>{plugin.dev_name}
) : (
✓ Report submitted
) : reviewReportOpen === r.id ? ( ) : replyOpen === r.id ? ( ) : ( <> {user && user.username === r.username ? ( ) : user && ( )} {isOwner && !r.developer_reply && !replyOpen && ( )} > )}No specific release notes for this update.
)}