(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
98
fork

Configure Feed

Select the types of activity you want to include in your feed.

at frontend-rewrite 53 lines 1.3 kB view raw
1import React from "react"; 2import { Link } from "react-router-dom"; 3 4interface RichTextProps { 5 text: string; 6 className?: string; 7} 8 9const MENTION_REGEX = 10 /(^|[\s(])@([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)/g; 11 12export default function RichText({ text, className }: RichTextProps) { 13 const parts: React.ReactNode[] = []; 14 let lastIndex = 0; 15 16 for (const match of text.matchAll(MENTION_REGEX)) { 17 const fullMatch = match[0]; 18 const prefix = match[1]; 19 const handle = match[2]; 20 const startIndex = match.index!; 21 22 if (startIndex > lastIndex) { 23 parts.push(text.slice(lastIndex, startIndex)); 24 } 25 26 if (prefix) { 27 parts.push(prefix); 28 } 29 30 parts.push( 31 <Link 32 key={startIndex} 33 to={`/profile/${handle}`} 34 className="text-primary-600 dark:text-primary-400 hover:underline" 35 onClick={(e) => e.stopPropagation()} 36 > 37 @{handle} 38 </Link>, 39 ); 40 41 lastIndex = startIndex + fullMatch.length; 42 } 43 44 if (lastIndex < text.length) { 45 parts.push(text.slice(lastIndex)); 46 } 47 48 if (parts.length === 0) { 49 return <span className={className}>{text}</span>; 50 } 51 52 return <span className={className}>{parts}</span>; 53}