1- import { useContext , useEffect , useState } from 'react' ;
1+ import { useContext , useEffect , useRef , useState } from 'react' ;
22import Popup from 'reactjs-popup' ;
33import { useLocation } from 'wouter' ;
44import { ClientConfigContext } from '../state/config' ;
@@ -13,6 +13,8 @@ function Footer() {
1313 const [ modeState , setModeState ] = useState < ThemeMode > ( 'system' ) ;
1414 const config = useContext ( ClientConfigContext ) ;
1515 const footerHtml = config . get < string > ( 'footer' ) ;
16+ const footerHtmlRef = useRef < HTMLDivElement | null > ( null ) ;
17+ const mountedScriptNodesRef = useRef < HTMLScriptElement [ ] > ( [ ] ) ;
1618 const loginEnabled = config . getBoolean ( 'login.enabled' ) ;
1719 const [ doubleClickTimes , setDoubleClickTimes ] = useState ( 0 ) ;
1820 useEffect ( ( ) => {
@@ -21,6 +23,46 @@ function Footer() {
2123 setMode ( mode ) ;
2224 } , [ ] )
2325
26+ useEffect ( ( ) => {
27+ const container = footerHtmlRef . current ;
28+ if ( ! container ) {
29+ return ;
30+ }
31+
32+ mountedScriptNodesRef . current . forEach ( ( script ) => script . remove ( ) ) ;
33+ mountedScriptNodesRef . current = [ ] ;
34+ container . replaceChildren ( ) ;
35+
36+ if ( ! footerHtml ) {
37+ return ;
38+ }
39+
40+ const template = document . createElement ( 'template' ) ;
41+ template . innerHTML = footerHtml ;
42+
43+ const scripts = Array . from ( template . content . querySelectorAll ( 'script' ) ) ;
44+ scripts . forEach ( ( script ) => script . remove ( ) ) ;
45+
46+ container . appendChild ( template . content . cloneNode ( true ) ) ;
47+
48+ scripts . forEach ( ( script ) => {
49+ const nextScript = document . createElement ( 'script' ) ;
50+
51+ Array . from ( script . attributes ) . forEach ( ( attribute ) => {
52+ nextScript . setAttribute ( attribute . name , attribute . value ) ;
53+ } ) ;
54+
55+ nextScript . textContent = script . textContent ;
56+ container . appendChild ( nextScript ) ;
57+ mountedScriptNodesRef . current . push ( nextScript ) ;
58+ } ) ;
59+
60+ return ( ) => {
61+ mountedScriptNodesRef . current . forEach ( ( script ) => script . remove ( ) ) ;
62+ mountedScriptNodesRef . current = [ ] ;
63+ } ;
64+ } , [ footerHtml ] )
65+
2466 const setMode = ( mode : ThemeMode ) => {
2567 setModeState ( mode ) ;
2668 localStorage . setItem ( 'theme' , mode ) ;
@@ -47,7 +89,7 @@ function Footer() {
4789 < link rel = "alternate" type = "application/json" title = { siteName } href = "/rss.json" />
4890 </ Helmet >
4991 < div className = "flex flex-col mb-8 space-y-2 justify-center items-center t-primary ani-show" >
50- { footerHtml && < div dangerouslySetInnerHTML = { { __html : footerHtml } } /> }
92+ < div ref = { footerHtmlRef } />
5193 < p className = 'text-sm text-neutral-500 font-normal link-line' >
5294 < span onDoubleClick = { ( ) => {
5395 if ( doubleClickTimes >= 2 ) { // actually need 3 times doubleClick
@@ -115,4 +157,4 @@ function ThemeButton({ current, mode, label, icon, onClick }: { current: ThemeMo
115157 </ button > )
116158}
117159
118- export default Footer ;
160+ export default Footer ;
0 commit comments