import * as React from 'react'; import { Issue } from './reportManagerSchema'; import { getLabelColors } from './reportManagerUtils'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; /** * Mini components to render issues. */ interface IssueCardProps { issue: Issue; onSelect: () => void; } // Component for the issue cards list on the left export const IssueCard = ({ issue, onSelect }: IssueCardProps) => { return (
{issue.labels.map(label => { const labelString = typeof label === 'string' ? label : label.name ?? ''; const colors = getLabelColors(labelString); return ; })}

{issue.title}

); }; interface IssueViewProps { issue: Issue; } // Detailed issue view that displays on the right export const IssueView = ({ issue }: IssueViewProps) => { const [issueBody, setIssueBody] = React.useState(''); // Parses the issue body into a formatted markdown (main functionality is replacing urls with tags) const parseBody = async (body: string) => { const imgTagRegex = /]*\/?>/; const videoTagRegex = /]*\/?>/; const audioTagRegex = /]*\/?>/; const fileRegex = /https:\/\/browndash\.com\/files/; const parts = body.split('\n'); const modifiedParts = await Promise.all( parts.map(async part => { if (imgTagRegex.test(part) || videoTagRegex.test(part) || audioTagRegex.test(part)) { return `\n${await parseFileTag(part)}\n`; } else if (fileRegex.test(part)) { const tag = await parseDashFiles(part); return tag; } else { return part; } }) ); setIssueBody(modifiedParts.join('\n')); }; // Extracts the src from an image tag and either returns the raw url if not accessible or a new image tag const parseFileTag = async (tag: string): Promise => { const regex = /src="([^"]+)"/; let url = ''; const match = tag.match(regex); if (!match) return tag; url = match[1]; if (!url) return tag; const mimeType = url.split('.').pop(); if (!mimeType) return tag; switch (mimeType) { // image case '.jpg': case '.png': case '.jpeg': case '.gif': return await getDisplayedFile(url, 'image'); // video case '.mp4': case '.mpeg': case '.webm': case '.mov': return await getDisplayedFile(url, 'video'); //audio case '.mp3': case '.wav': case '.ogg': return await getDisplayedFile(url, 'audio'); } return tag; }; // Returns the corresponding HTML tag for a src url const parseDashFiles = async (url: string) => { const dashImgRegex = /https:\/\/browndash\.com\/files[/\\]images/; const dashVideoRegex = /https:\/\/browndash\.com\/files[/\\]videos/; const dashAudioRegex = /https:\/\/browndash\.com\/files[/\\]audio/; if (dashImgRegex.test(url)) { return await getDisplayedFile(url, 'image'); } else if (dashVideoRegex.test(url)) { return await getDisplayedFile(url, 'video'); } else if (dashAudioRegex.test(url)) { return await getDisplayedFile(url, 'audio'); } else { return url; } }; const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise => { switch (fileType) { case 'image': const imgValid = await isImgValid(url); if (!imgValid) return `\n${url} (This image could not be loaded)\n`; return `\n${url}\nIssue asset\n`; case 'video': const videoValid = await isVideoValid(url); if (!videoValid) return `\n${url} (This video could not be loaded)\n`; return `\n${url}\n