diff --git a/commafeed-client/package-lock.json b/commafeed-client/package-lock.json
index 67209b8a..3ca6dd5f 100644
--- a/commafeed-client/package-lock.json
+++ b/commafeed-client/package-lock.json
@@ -33,6 +33,7 @@
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-ga4": "^2.1.0",
+ "react-helmet": "^6.1.0",
"react-icons": "^5.2.1",
"react-infinite-scroller": "^1.2.6",
"react-redux": "^9.1.2",
@@ -51,6 +52,7 @@
"@types/mousetrap": "^1.6.15",
"@types/react": "^18.2.78",
"@types/react-dom": "^18.3.0",
+ "@types/react-helmet": "^6.1.11",
"@types/react-infinite-scroller": "^1.2.5",
"@types/swagger-ui-react": "^4.18.3",
"@types/throttle-debounce": "^5.0.2",
@@ -2079,6 +2081,15 @@
"@types/react": "*"
}
},
+ "node_modules/@types/react-helmet": {
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
+ "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/react-infinite-scroller": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/react-infinite-scroller/-/react-infinite-scroller-1.2.5.tgz",
@@ -7667,11 +7678,30 @@
"react-dom": ">= 16.3.0"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
"node_modules/react-ga4": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz",
"integrity": "sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ=="
},
+ "node_modules/react-helmet": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
+ "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.1.1",
+ "react-side-effect": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0"
+ }
+ },
"node_modules/react-icons": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
@@ -7826,6 +7856,14 @@
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-side-effect": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
+ "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
+ "peerDependencies": {
+ "react": "^16.3.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
diff --git a/commafeed-client/package.json b/commafeed-client/package.json
index e93d3cdd..b7d9b842 100644
--- a/commafeed-client/package.json
+++ b/commafeed-client/package.json
@@ -39,6 +39,7 @@
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-ga4": "^2.1.0",
+ "react-helmet": "^6.1.0",
"react-icons": "^5.2.1",
"react-infinite-scroller": "^1.2.6",
"react-redux": "^9.1.2",
@@ -57,6 +58,7 @@
"@types/mousetrap": "^1.6.15",
"@types/react": "^18.2.78",
"@types/react-dom": "^18.3.0",
+ "@types/react-helmet": "^6.1.11",
"@types/react-infinite-scroller": "^1.2.5",
"@types/swagger-ui-react": "^4.18.3",
"@types/throttle-debounce": "^5.0.2",
diff --git a/commafeed-client/src/App.tsx b/commafeed-client/src/App.tsx
index 57ec7af8..45d2393a 100644
--- a/commafeed-client/src/App.tsx
+++ b/commafeed-client/src/App.tsx
@@ -1,7 +1,6 @@
import { i18n } from "@lingui/core"
import { I18nProvider } from "@lingui/react"
import { MantineProvider } from "@mantine/core"
-import { useDidUpdate } from "@mantine/hooks"
import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications"
import { Constants } from "app/constants"
@@ -29,8 +28,9 @@ import { LoginPage } from "pages/auth/LoginPage"
import { PasswordRecoveryPage } from "pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "pages/auth/RegistrationPage"
import { WelcomePage } from "pages/WelcomePage"
-import React, { useEffect, useRef } from "react"
+import React, { useEffect } from "react"
import ReactGA from "react-ga4"
+import { Helmet } from "react-helmet"
import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"
import Tinycon from "tinycon"
@@ -166,38 +166,13 @@ function BrowserExtensionBadgeUnreadCountHandler() {
return null
}
-function CustomJs() {
- const scriptLoaded = useRef(false)
-
- // useDidUpdate is used instead of useEffect because we want to skip the first render
- // the first render is the render of react-router, the routes are actually loaded in a second render
- // we want the script to be executed when the first route is done loading
- useDidUpdate(() => {
- if (scriptLoaded.current) {
- return
- }
-
- const script = document.createElement("script")
- script.src = "custom_js.js"
- script.async = true
- document.body.appendChild(script)
-
- scriptLoaded.current = true
- })
-
- return null
-}
-
-function CustomCss() {
- useEffect(() => {
- const link = document.createElement("link")
- link.rel = "stylesheet"
- link.type = "text/css"
- link.href = "custom_css.css"
- document.head.appendChild(link)
- }, [])
-
- return null
+function CustomCode() {
+ return (
+
+
+
+
+ )
}
export function App() {
@@ -217,8 +192,7 @@ export function App() {
-
-
+
>