diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 4ac2680fa..b212045e6 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -86,7 +86,6 @@ import GiveawayModal from './premium/GiveawayModal.async'; import PremiumMainModal from './premium/PremiumMainModal.async'; import StarsGiftingPickerModal from './premium/StarsGiftingPickerModal.async'; import SafeLinkModal from './SafeLinkModal.async'; -import SvgController from './SvgController'; import ConfettiContainer from './visualEffects/ConfettiContainer'; import SnapEffectContainer from './visualEffects/SnapEffectContainer'; import WaveContainer from './visualEffects/WaveContainer'; @@ -588,7 +587,6 @@ const Main = ({ - ); }; diff --git a/src/components/main/SvgController.tsx b/src/components/main/SvgController.tsx deleted file mode 100644 index 725c77aa6..000000000 --- a/src/components/main/SvgController.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { memo, useEffect } from '../../lib/teact/teact'; - -import { createCallbackManager } from '../../util/callbacks'; -import generateUniqueId from '../../util/generateUniqueId'; - -import useForceUpdate from '../../hooks/useForceUpdate'; - -import Portal from '../ui/Portal'; - -const DEFINITION_MAP = new Map(); -const CALLBACK_MANAGER = createCallbackManager(); - -const SvgController = () => { - const forceUpdate = useForceUpdate(); - - useEffect(() => { - return CALLBACK_MANAGER.addCallback(forceUpdate); - }, []); - - return ( - - - - {Array.from(DEFINITION_MAP.values())} - - - - ); -}; - -export default memo(SvgController); - -export function addSvgDefinition(element: React.ReactElement, id?: string) { - id ??= generateUniqueId(); - element.props.id = id; - - DEFINITION_MAP.set(element.props.id, element); - CALLBACK_MANAGER.runCallbacks(); - return id; -} - -export function removeSvgDefinition(id: string) { - DEFINITION_MAP.delete(id); - CALLBACK_MANAGER.runCallbacks(); -} diff --git a/src/components/main/visualEffects/WaveContainer.tsx b/src/components/main/visualEffects/WaveContainer.tsx index 15623c1ac..3c62ac6ee 100644 --- a/src/components/main/visualEffects/WaveContainer.tsx +++ b/src/components/main/visualEffects/WaveContainer.tsx @@ -9,12 +9,11 @@ import { SVG_NAMESPACE } from '../../../config'; import { selectTabState } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import buildStyle from '../../../util/buildStyle'; +import { addSvgDefinition, removeSvgDefinition } from '../../../util/svgController'; import windowSize from '../../../util/windowSize'; import useLastCallback from '../../../hooks/useLastCallback'; -import { addSvgDefinition, removeSvgDefinition } from '../SvgController'; - import styles from './WaveContainer.module.scss'; import waveRipple from '../../../assets/wave_ripple.svg'; diff --git a/src/hooks/stickers/useColorFilter.tsx b/src/hooks/stickers/useColorFilter.tsx index e4ec3075e..f789263e6 100644 --- a/src/hooks/stickers/useColorFilter.tsx +++ b/src/hooks/stickers/useColorFilter.tsx @@ -1,10 +1,9 @@ import React, { useEffect } from '../../lib/teact/teact'; import { SVG_NAMESPACE } from '../../config'; +import { addSvgDefinition, removeSvgDefinition } from '../../util/svgController'; import { hexToRgb } from '../../util/switchTheme'; -import { addSvgDefinition, removeSvgDefinition } from '../../components/main/SvgController'; - const SVG_MAP = new Map(); class SvgColorFilter { diff --git a/src/util/svgController.ts b/src/util/svgController.ts new file mode 100644 index 000000000..8068232c4 --- /dev/null +++ b/src/util/svgController.ts @@ -0,0 +1,47 @@ +import { SVG_NAMESPACE } from '../config'; +import { requestMutation } from '../lib/fasterdom/fasterdom'; +import jsxToHtml from './element/jsxToHtml'; +import generateUniqueId from './generateUniqueId'; + +const DEFINITION_MAP = new Map(); + +let defs: SVGElement | undefined; + +function init() { + if (defs) return; + + const container = document.createElementNS(SVG_NAMESPACE, 'svg'); + container.setAttribute('width', '0'); + container.setAttribute('height', '0'); + container.setAttribute('viewBox', '0 0 1 1'); + container.classList.add('svg-definitions'); + document.body.appendChild(container); + + defs = document.createElementNS(SVG_NAMESPACE, 'defs'); + container.appendChild(defs); +} + +function appendElement(element: Element) { + requestMutation(() => { + if (!defs) init(); + defs!.appendChild(element); + }); +} + +export function addSvgDefinition(element: React.JSX.Element, id?: string) { + id ??= generateUniqueId(); + element.props.id = id; + + const htmlElement = jsxToHtml(element)[0]; + DEFINITION_MAP.set(element.props.id, htmlElement); + appendElement(htmlElement); + return id; +} + +export function removeSvgDefinition(id: string) { + const element = DEFINITION_MAP.get(id); + if (element) { + element.remove(); + DEFINITION_MAP.delete(id); + } +}