Skip to content

Commit 92d3735

Browse files
authored
fix: execute footer html scripts (#476)
1 parent 77c3acc commit 92d3735

1 file changed

Lines changed: 45 additions & 3 deletions

File tree

client/src/components/footer.tsx

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useContext, useEffect, useState } from 'react';
1+
import { useContext, useEffect, useRef, useState } from 'react';
22
import Popup from 'reactjs-popup';
33
import { useLocation } from 'wouter';
44
import { 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

Comments
 (0)