@@ -9,6 +9,60 @@ import dataService from "../../services/dataService";
99import { useAuthContext } from "../../contexts/AuthContext" ;
1010import { useEventsContext } from "../../contexts/EventsContext" ;
1111
12+ // URLRegex matches strings that look like URLs, including:
13+ // 1. URLs starting with "http://" or "https://" (e.g., "https://example.com/path")
14+ // 2. URLs starting with "www." (e.g., "www.example.com")
15+ // 3. Bare domain names without protocol or www (e.g., "example.com")
16+
17+ const URLRegex =
18+ / ( h t t p s ? : \/ \/ [ ^ \s ] + | w w w \. [ ^ \s ] + | [ a - z A - Z 0 - 9 - ] + \. [ a - z A - Z ] { 2 , } (?: [ ^ \s ] * ) ) / g;
19+
20+ const LinkText = ( { children } ) => {
21+ if ( typeof children !== "string" )
22+ throw new Error ( "LinkText can only accept a string as children" ) ;
23+
24+ const separated = children . split ( URLRegex ) ;
25+
26+ return separated . map ( ( res , index ) => {
27+ if ( URLRegex . test ( res ) ) {
28+ let href ;
29+
30+ // Determine href and always enforce HTTPS
31+ if ( res . startsWith ( "https://" ) ) {
32+ href = res ;
33+ } else if ( res . startsWith ( "http://" ) ) {
34+ href = res . replace ( / ^ h t t p : \/ \/ / , "https://" ) ;
35+ } else if ( res . startsWith ( "www." ) ) {
36+ href = `https://${ res } ` ;
37+ } else {
38+ href = `https://${ res } ` ;
39+ }
40+
41+ // Convert Unicode domain to punycode for display
42+ let displayURL ;
43+ try {
44+ const urlObj = new URL ( href ) ;
45+ displayURL = urlObj . toString ( ) ; // toString() converts hostname to punycode
46+ } catch {
47+ displayURL = href ; // fallback if URL parsing fails
48+ }
49+
50+ return (
51+ < a
52+ key = { `${ res } -${ index } ` }
53+ href = { href }
54+ target = "_blank"
55+ rel = "noreferrer"
56+ className = "text-blue-600 underline hover:text-blue-800"
57+ >
58+ { displayURL }
59+ </ a >
60+ ) ;
61+ }
62+ return res ;
63+ } ) ;
64+ } ;
65+
1266const EventModal = ( ) => {
1367 const { setEvents } = useEventsContext ( ) ;
1468 const modal = useModalContext ( ) ;
@@ -94,7 +148,9 @@ const EventModal = () => {
94148 </ div >
95149 < h3 className = "mb-0" > Description:</ h3 > { " " }
96150 < div className = "description break-words w-auto min-h-20 mb-2 p-2 border-solid border-black border-2 font-semibold rounded-xl bg-neutral-200/50" >
97- < p > { modal . activeEvent . description } </ p >
151+ < p >
152+ < LinkText > { modal . activeEvent . description } </ LinkText >
153+ </ p >
98154 </ div >
99155 < div >
100156 { /* <section className="flex m-3 gap-1 font-semibold">
@@ -106,7 +162,17 @@ const EventModal = () => {
106162 </section> */ }
107163 < section className = "flex m-3 gap-1 font-semibold" >
108164 < IoLocationOutline className = "mt-1" /> { " " }
109- < span > Location: { modal . activeEvent . location } </ span >
165+ < span >
166+ Location:{ " " }
167+ < a
168+ href = { modal . activeEvent . location }
169+ target = "_blank"
170+ rel = "noopener noreferrer"
171+ className = "text-blue-600 underline hover:text-blue-800"
172+ >
173+ { modal . activeEvent . location }
174+ </ a >
175+ </ span >
110176 </ section >
111177 { user ? (
112178 < >
0 commit comments