11import React , { useEffect , useState } from 'react' ;
2- import { IconWebhook , IconChevronRight , Icon } from '@tabler/icons-react'
2+ import { IconChevronRight , Icon as TablerIcon } from '@tabler/icons-react'
33
44interface ExternLinkProps {
55 href : string ;
6- icon ?: React . ReactNode | React . ElementType ;
6+ icon ?: React . ReactNode | React . ElementType ; // Erlaubt sowohl React-Komponenten als auch Strings
77 manualTitle ?: string ; // Neuer Prop für manuellen Titel
88}
99
@@ -14,64 +14,64 @@ interface LinkMetaData {
1414}
1515
1616const fetchMetaData = async ( url : string ) : Promise < LinkMetaData > => {
17- try {
18- const response = await fetch ( url ) ;
19- if ( ! response . ok ) {
20- throw new Error ( 'Network response was not ok' ) ;
21- }
22- const text = await response . text ( ) ; // Hole den HTML-Inhalt
23- const parser = new DOMParser ( ) ;
24- const doc = parser . parseFromString ( text , 'text/html' ) ;
25-
26- const title = doc . querySelector ( 'meta[property="og:title"]' ) ?. getAttribute ( 'content' ) ||
27- doc . querySelector ( 'title' ) ?. textContent ||
28- 'No title' ;
29- const description = doc . querySelector ( 'meta[property="og:description"]' ) ?. getAttribute ( 'content' ) ||
30- doc . querySelector ( 'meta[name="description"]' ) ?. getAttribute ( 'content' ) ||
31- 'No description' ;
32- const icon = doc . querySelector ( 'link[rel="icon"]' ) ?. getAttribute ( 'href' ) ||
33- doc . querySelector ( 'link[rel="shortcut icon"]' ) ?. getAttribute ( 'href' ) ||
34- 'https://example.com/default-icon.png' ;
35-
36- return { title, description, icon : new URL ( icon , url ) . href } ;
37- } catch ( error ) {
38- console . error ( 'Error fetching metadata:' , error ) ;
39- return {
40- title : 'Error' ,
41- description : 'Unable to fetch metadata.' ,
42- icon : 'https://example.com/error-icon.png' ,
43- } ;
17+ try {
18+ const response = await fetch ( url ) ;
19+ if ( ! response . ok ) {
20+ throw new Error ( 'Network response was not ok' ) ;
4421 }
45- } ;
46-
47- const ExternLink : React . FC < ExternLinkProps > = ( { href, icon, manualTitle } ) => {
48- const [ metaData , setMetaData ] = useState < LinkMetaData | null > ( null ) ;
49- const [ loading , setLoading ] = useState ( true ) ;
22+ const text = await response . text ( ) ; // Hole den HTML-Inhalt
23+ const parser = new DOMParser ( ) ;
24+ const doc = parser . parseFromString ( text , 'text/html' ) ;
25+
26+ const title = doc . querySelector ( 'meta[property="og:title"]' ) ?. getAttribute ( 'content' ) ||
27+ doc . querySelector ( 'title' ) ?. textContent ||
28+ 'No title' ;
29+ const description = doc . querySelector ( 'meta[property="og:description"]' ) ?. getAttribute ( 'content' ) ||
30+ doc . querySelector ( 'meta[name="description"]' ) ?. getAttribute ( 'content' ) ||
31+ 'No description' ;
32+ const icon = doc . querySelector ( 'link[rel="icon"]' ) ?. getAttribute ( 'href' ) ||
33+ doc . querySelector ( 'link[rel="shortcut icon"]' ) ?. getAttribute ( 'href' ) ||
34+ 'https://example.com/default-icon.png' ;
35+
36+ return { title, description, icon : new URL ( icon , url ) . href } ;
37+ } catch ( error ) {
38+ console . error ( 'Error fetching metadata:' , error ) ;
39+ return {
40+ title : 'Error' ,
41+ description : 'Unable to fetch metadata.' ,
42+ icon : 'https://example.com/error-icon.png' ,
43+ } ;
44+ }
45+ } ;
5046
51- useEffect ( ( ) => {
52- if ( ! manualTitle ) {
53- fetchMetaData ( href ) . then ( ( data ) => {
54- setMetaData ( data ) ;
55- setLoading ( false ) ;
56- } ) ;
57- } else {
58- setMetaData ( {
59- title : manualTitle ,
60- description : 'No description available' ,
61- icon : 'https://example.com/web-icon.png' , // Web-Icon für manuellen Titel
62- } ) ;
47+ const ExternLink : React . FC < ExternLinkProps > = ( { href, icon, manualTitle } ) => {
48+ const [ metaData , setMetaData ] = useState < LinkMetaData | null > ( null ) ;
49+ const [ loading , setLoading ] = useState ( true ) ;
50+
51+ useEffect ( ( ) => {
52+ if ( ! manualTitle ) {
53+ fetchMetaData ( href ) . then ( ( data ) => {
54+ setMetaData ( data ) ;
6355 setLoading ( false ) ;
64- }
65- } , [ href , manualTitle ] ) ;
66-
67- if ( loading ) {
68- return < div > Loading...</ div > ; // Display a loading state while fetching metadata
56+ } ) ;
57+ } else {
58+ setMetaData ( {
59+ title : manualTitle ,
60+ description : 'No description available' ,
61+ icon : 'https://example.com/web-icon.png' , // Web-Icon für manuellen Titel
62+ } ) ;
63+ setLoading ( false ) ;
6964 }
65+ } , [ href , manualTitle ] ) ;
66+
67+ if ( loading ) {
68+ return < div > Loading...</ div > ; // Display a loading state while fetching metadata
69+ }
7070
7171 return (
7272 < a
7373 href = { href }
74- className = "w-full border border-gray-200 shadow-sm hover:shadow-md dark:border-neutral-700 dark:hover:border-neutral-600 transition-all duration-200 dark:bg-neutral-900 bg-white rounded-lg overflow-hidden flex flex-col justify-start relative my-4 "
74+ className = "w-full border border-gray-200 shadow-sm hover:shadow-md dark:border-neutral-700 dark:hover:border-neutral-600 transition-all duration-200 dark:bg-neutral-900 bg-white rounded-lg overflow-hidden flex flex-col justify-start relative"
7575 target = "_blank"
7676 rel = "noopener noreferrer"
7777 >
@@ -81,7 +81,11 @@ const fetchMetaData = async (url: string): Promise<LinkMetaData> => {
8181 typeof icon === 'string' ? (
8282 < img src = { icon } alt = "Favicon" className = "w-6 h-6 mr-3" />
8383 ) : (
84- React . createElement ( icon , { className : 'w-6 h-6 mr-3' } )
84+ React . isValidElement ( icon ) ? (
85+ React . cloneElement ( icon as React . ReactElement , { className : 'w-6 h-6 mr-3' } )
86+ ) : (
87+ React . createElement ( icon as React . ElementType , { className : 'w-6 h-6 mr-3' } )
88+ )
8589 )
8690 ) : (
8791 < img src = { metaData ?. icon } alt = "Favicon" className = "w-6 h-6 mr-3" />
0 commit comments