You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/hooks/useIconToImageURL.tsx

142 lines
3.9 KiB
TypeScript

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 } = props;
let { iconColor, backgroundColor } = props;
if (!backgroundColor) {
backgroundColor = chooseIconColors(
isThemed,
isDarkTheme,
'--text-primary-color',
'--background-primary-color',
'--white-color'
);
}
if (!iconColor) {
iconColor = 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(
<SessionIcon
iconType={iconType}
iconSize={iconSize}
iconColor={iconColor}
backgroundColor={backgroundColor}
/>
);
// 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: backgroundColor,
fgColor: iconColor,
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 };
};