diff --git a/app/components/post_list/post/body/message/message.tsx b/app/components/post_list/post/body/message/message.tsx index d8101ce8d..0cffaecc1 100644 --- a/app/components/post_list/post/body/message/message.tsx +++ b/app/components/post_list/post/body/message/message.tsx @@ -67,7 +67,12 @@ const Message = ({currentUser, isHighlightWithoutNotificationLicensed, highlight const blockStyles = getMarkdownBlockStyles(theme); const textStyles = getMarkdownTextStyles(theme); - const onLayout = useCallback((event: LayoutChangeEvent) => setHeight(event.nativeEvent.layout.height), []); + const onLayout = useCallback((event: LayoutChangeEvent) => { + const h = event.nativeEvent.layout.height; + if (h > maxHeight) { + setHeight(event.nativeEvent.layout.height); + } + }, [maxHeight]); const onPress = () => setOpen(!open); const channelMentions = useMemo(() => { diff --git a/app/context/theme/index.tsx b/app/context/theme/index.tsx index c01a096a1..e8e172e17 100644 --- a/app/context/theme/index.tsx +++ b/app/context/theme/index.tsx @@ -6,6 +6,7 @@ import React, {type ComponentType, createContext, useEffect, useState} from 'rea import {Appearance} from 'react-native'; import {Preferences} from '@constants'; +import useDidUpdate from '@hooks/did_update'; import {queryThemePreferences} from '@queries/servers/preference'; import {observeCurrentTeamId} from '@queries/servers/system'; import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme'; @@ -33,22 +34,43 @@ export function getDefaultThemeByAppearance(): Theme { export const ThemeContext = createContext(getDefaultThemeByAppearance()); const {Consumer, Provider} = ThemeContext; +const themeCache = new Map(); +let clearingThemeCache = false; +const clearThemeCache = () => { + if (clearingThemeCache) { + return; + } + clearingThemeCache = true; + themeCache.clear(); + setTimeout(() => { + // We set this timeout to avoid clearing the cache multiple times + // this would happen as we have a Provider for each screen in the stack + // and when the themes changes we only want to invalidate the cache once + clearingThemeCache = false; + }, 300); +}; + const getTheme = (teamId: string | undefined, themes: PreferenceModel[]): Theme => { - if (teamId) { - const teamTheme = themes.find((t) => t.name === teamId) || themes[0]; - if (teamTheme?.value) { - try { - const theme = setThemeDefaults(JSON.parse(teamTheme.value)); - return theme; - } catch { - // no theme change - } - } + if (teamId && themeCache.has(teamId)) { + return themeCache.get(teamId)!; } - const defaultTheme = getDefaultThemeByAppearance(); + const newTheme = (() => { + if (teamId) { + const teamTheme = themes.find((t) => t.name === teamId) || themes[0]; + if (teamTheme?.value) { + try { + return setThemeDefaults(JSON.parse(teamTheme.value)); + } catch { + // no theme change + } + } + } + return getDefaultThemeByAppearance(); + })(); - return defaultTheme; + themeCache.set(teamId || 'default', newTheme); + return newTheme; }; const ThemeProvider = ({currentTeamId, children, themes}: Props) => { @@ -69,7 +91,11 @@ const ThemeProvider = ({currentTeamId, children, themes}: Props) => { updateThemeIfNeeded(theme); }, [theme]); - useEffect(() => { + useDidUpdate(() => { + clearThemeCache(); + }, [themes]); + + useDidUpdate(() => { setTheme(getTheme(currentTeamId, themes)); }, [currentTeamId, themes]); diff --git a/app/screens/settings/display_theme/display_theme.tsx b/app/screens/settings/display_theme/display_theme.tsx index 19b0a6bda..1627ea8c9 100644 --- a/app/screens/settings/display_theme/display_theme.tsx +++ b/app/screens/settings/display_theme/display_theme.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback, useMemo, useState, useEffect} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {savePreference} from '@actions/remote/preference'; import SettingContainer from '@components/settings/container'; @@ -9,6 +9,7 @@ import {Preferences} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; +import useDidUpdate from '@hooks/did_update'; import {popTopScreen} from '@screens/navigation'; import CustomTheme from './custom_theme'; @@ -30,16 +31,6 @@ const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUser const close = () => popTopScreen(componentId); - useEffect(() => { - const differentTheme = theme.type?.toLowerCase() !== newTheme?.toLowerCase(); - - if (!differentTheme) { - close(); - return; - } - setThemePreference(); - }, [newTheme]); - const setThemePreference = useCallback(() => { const allowedTheme = allowedThemeKeys.find((tk) => tk === newTheme); const themeJson = Preferences.THEMES[allowedTheme as ThemeKey] || initialTheme; @@ -51,7 +42,17 @@ const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUser value: JSON.stringify(themeJson), }; savePreference(serverUrl, [pref]); - }, [allowedThemeKeys, currentTeamId, theme.type, serverUrl, newTheme]); + }, [allowedThemeKeys, initialTheme, currentTeamId, currentUserId, serverUrl, newTheme]); + + useDidUpdate(() => { + const differentTheme = theme.type?.toLowerCase() !== newTheme?.toLowerCase(); + + if (!differentTheme) { + close(); + return; + } + setThemePreference(); + }, [close, newTheme, setThemePreference, theme.type]); const onAndroidBack = () => { setThemePreference();