import { useCallback, useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; import useMount from 'react-use/lib/useMount'; import { SessionIcon, SessionIconProps, SessionIconType } from '../components/icon'; import { sleepFor } from '../session/utils/Promise'; import { useIsDarkTheme } from '../state/selectors/theme'; import { ThemeKeys, getThemeValue } from '../themes/globals'; const chooseIconColors = ( isThemed: boolean, isDarkTheme: boolean, darkColor: ThemeKeys, lightColor: ThemeKeys, fallbackColor: ThemeKeys ) => { return getThemeValue(isThemed ? (isDarkTheme ? darkColor : lightColor) : fallbackColor); }; const convertIconToImageURL = async ( props: { isThemed: boolean; isDarkTheme: boolean } & Pick< SessionIconProps, 'iconType' | 'iconSize' | 'iconColor' | 'backgroundColor' > ): Promise<{ dataUrl: string; bgColor: string; fgColor: string }> => { const { isThemed, isDarkTheme, iconType, iconSize, iconColor, backgroundColor } = props; let bgColor = backgroundColor; let fgColor = iconColor; if (!bgColor) { bgColor = chooseIconColors( isThemed, isDarkTheme, '--text-primary-color', '--background-primary-color', '--white-color' ); } if (!fgColor) { fgColor = chooseIconColors( isThemed, isDarkTheme, '--background-primary-color', '--text-primary-color', '--black-color' ); } const root = document.querySelector('#root'); const divElement = document.createElement('div'); divElement.id = 'icon-to-image-url'; root?.appendChild(divElement); const reactRoot = createRoot(divElement!); reactRoot.render( ); // wait for it to render await sleepFor(100); const svg = root?.querySelector(`#icon-to-image-url svg`); svg?.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); const svgString = svg?.outerHTML; reactRoot?.unmount(); root?.removeChild(divElement); return { bgColor, fgColor, dataUrl: svgString ? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}` : '', }; }; export const useIconToImageURL = ({ iconType, iconSize, isThemed = true, }: { iconType: SessionIconType; iconSize: number; isThemed?: boolean; }) => { const isDarkTheme = useIsDarkTheme(); const [dataURL, setDataURL] = useState(''); const [loading, setLoading] = useState(false); const [mounted, setMounted] = useState(false); const [inDarkTheme, setInDarkTheme] = useState(false); const [backgroundColor, setBackgroundColor] = useState(''); const [iconColor, setIconColor] = useState(''); const loadURL = useCallback(async () => { setLoading(true); setDataURL(''); try { const { dataUrl: newURL, bgColor, fgColor, } = await convertIconToImageURL({ isThemed, isDarkTheme, iconType, iconSize, iconColor, backgroundColor, }); if (!newURL) { throw new Error('[useIconToImageURL] Failed to convert icon to URL'); } setBackgroundColor(bgColor); setIconColor(fgColor); setDataURL(newURL); if (!mounted) { setMounted(true); } setLoading(false); setInDarkTheme(!!isThemed && isDarkTheme); } catch (error) { window.log.error('[useIconToImageURL] Error fetching icon data url', error); } }, [backgroundColor, iconColor, iconSize, iconType, isDarkTheme, isThemed, mounted]); useMount(() => { void loadURL(); }); useEffect(() => { if (!loading && mounted && isThemed && isDarkTheme !== inDarkTheme) { void loadURL(); } }, [inDarkTheme, isDarkTheme, isThemed, loadURL, loading, mounted]); return { dataURL, iconSize, iconColor, backgroundColor, loading }; };