From e965265b7e2b8f108e795422357fc19094a5bd40 Mon Sep 17 00:00:00 2001 From: Aryan-Verma-999 Date: Thu, 11 Dec 2025 20:48:33 +0530 Subject: [PATCH 1/2] feat: Add dark/light theme toggle button in ChatHeader --- packages/react/src/store/index.js | 1 + packages/react/src/store/themeStore.js | 9 +++++++++ .../react/src/views/ChatHeader/ChatHeader.js | 20 ++++++++++++++++--- packages/react/src/views/EmbeddedChat.js | 17 ++++++++++++---- .../src/components/Icon/icons/Moon.js | 14 +++++++++++++ .../src/components/Icon/icons/Sun.js | 14 +++++++++++++ .../src/components/Icon/icons/index.js | 4 ++++ 7 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 packages/react/src/store/themeStore.js create mode 100644 packages/ui-elements/src/components/Icon/icons/Moon.js create mode 100644 packages/ui-elements/src/components/Icon/icons/Sun.js diff --git a/packages/react/src/store/index.js b/packages/react/src/store/index.js index bddd0bd1d6..7264932058 100644 --- a/packages/react/src/store/index.js +++ b/packages/react/src/store/index.js @@ -11,3 +11,4 @@ export { default as useMentionsStore } from './mentionsStore'; export { default as usePinnedMessageStore } from './pinnedMessageStore'; export { default as useStarredMessageStore } from './starredMessageStore'; export { default as useSidebarStore } from './sidebarStore'; +export { default as useThemeStore } from './themeStore'; diff --git a/packages/react/src/store/themeStore.js b/packages/react/src/store/themeStore.js new file mode 100644 index 0000000000..72d5cd18a1 --- /dev/null +++ b/packages/react/src/store/themeStore.js @@ -0,0 +1,9 @@ +import { create } from 'zustand'; + +const useThemeStore = create((set) => ({ + isDarkMode: false, + setIsDarkMode: (isDarkMode) => set(() => ({ isDarkMode })), + toggleTheme: () => set((state) => ({ isDarkMode: !state.isDarkMode })), +})); + +export default useThemeStore; diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 9143598d30..7f61d41397 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -24,6 +24,7 @@ import { useStarredMessageStore, useFileStore, useSidebarStore, + useThemeStore, } from '../../store'; import { DynamicHeader } from '../DynamicHeader'; import useFetchChatData from '../../hooks/useFetchChatData'; @@ -40,7 +41,7 @@ const ChatHeader = ({ className = '', style = {}, optionConfig = { - surfaceItems: ['minmax', 'close'], + surfaceItems: ['minmax', 'theme', 'close'], menuItems: [ 'thread', 'mentions', @@ -103,6 +104,10 @@ const ChatHeader = ({ const filtered = useMessageStore((state) => state.filtered); const setFilter = useMessageStore((state) => state.setFilter); + // Theme toggle + const isDarkMode = useThemeStore((state) => state.isDarkMode); + const toggleTheme = useThemeStore((state) => state.toggleTheme); + const isThreadOpen = useMessageStore((state) => state.isThreadOpen); const threadMainMessage = useMessageStore((state) => state.threadMainMessage); @@ -244,6 +249,13 @@ const ChatHeader = ({ iconName: 'cross', visible: isClosable, }, + theme: { + label: isDarkMode ? 'Light Mode' : 'Dark Mode', + id: 'theme', + onClick: toggleTheme, + iconName: isDarkMode ? 'sun' : 'moon', + visible: true, + }, thread: { label: 'Threads', id: 'thread', @@ -312,6 +324,8 @@ const ChatHeader = ({ fullScreen, isClosable, isUserAuthenticated, + isDarkMode, + toggleTheme, handleLogout, setFullScreen, setClosableState, @@ -391,8 +405,8 @@ const ChatHeader = ({ isRoomTeam ? 'team' : isChannelPrivate - ? 'hash_lock' - : 'hash' + ? 'hash_lock' + : 'hash' } size={fullScreen ? '1.25rem' : '1rem'} /> diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index f3b94c7b48..81f7dca69d 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -18,7 +18,7 @@ import { import { ChatLayout } from './ChatLayout'; import { ChatHeader } from './ChatHeader'; import { RCInstanceProvider } from '../context/RCInstance'; -import { useUserStore, useLoginStore, useMessageStore } from '../store'; +import { useUserStore, useLoginStore, useMessageStore, useThemeStore } from '../store'; import DefaultTheme from '../theme/DefaultTheme'; import { getTokenStorage } from '../lib/auth'; import { styles } from './EmbeddedChat.styles'; @@ -34,7 +34,7 @@ const EmbeddedChat = (props) => { const { isClosable = false, - setClosableState = () => {}, + setClosableState = () => { }, width = '100%', height = '95vh', host = 'http://localhost:3000', @@ -64,6 +64,15 @@ const EmbeddedChat = (props) => { const { classNames, styleOverrides } = useComponentOverrides('EmbeddedChat'); const [fullScreen, setFullScreen] = useState(false); const [isSynced, setIsSynced] = useState(!remoteOpt); + + // Theme store for runtime theme toggle + const isDarkMode = useThemeStore((state) => state.isDarkMode); + const setIsDarkMode = useThemeStore((state) => state.setIsDarkMode); + + // Initialize theme store from prop on mount + useEffect(() => { + setIsDarkMode(dark); + }, [dark, setIsDarkMode]); const { getToken, saveToken, deleteToken } = getTokenStorage(secure); const { setIsUserAuthenticated, @@ -225,11 +234,11 @@ const EmbeddedChat = (props) => { if (!isSynced) return null; return ( - + ( + + + +); + +export default Moon; diff --git a/packages/ui-elements/src/components/Icon/icons/Sun.js b/packages/ui-elements/src/components/Icon/icons/Sun.js new file mode 100644 index 0000000000..6d340b64a5 --- /dev/null +++ b/packages/ui-elements/src/components/Icon/icons/Sun.js @@ -0,0 +1,14 @@ +import React from 'react'; + +const Sun = (props) => ( + + + +); + +export default Sun; diff --git a/packages/ui-elements/src/components/Icon/icons/index.js b/packages/ui-elements/src/components/Icon/icons/index.js index 234251edab..5930fface6 100644 --- a/packages/ui-elements/src/components/Icon/icons/index.js +++ b/packages/ui-elements/src/components/Icon/icons/index.js @@ -64,6 +64,8 @@ import Avatar from './Avatar'; import FormatText from './FormatText'; import Cog from './Cog'; import Team from './Team'; +import Sun from './Sun'; +import Moon from './Moon'; const icons = { file: File, @@ -132,6 +134,8 @@ const icons = { avatar: Avatar, 'format-text': FormatText, cog: Cog, + sun: Sun, + moon: Moon, }; export default icons; From 453e829a2c69961a15c45ef2af1476841ae39ed4 Mon Sep 17 00:00:00 2001 From: Aryan-Verma-999 Date: Thu, 11 Dec 2025 21:05:55 +0530 Subject: [PATCH 2/2] chore: format code with prettier --- packages/react/src/store/themeStore.js | 6 +++--- .../react/src/views/ChatHeader/ChatHeader.js | 4 ++-- packages/react/src/views/EmbeddedChat.js | 14 +++++++++++--- .../src/components/Icon/icons/Moon.js | 16 ++++++++-------- .../ui-elements/src/components/Icon/icons/Sun.js | 16 ++++++++-------- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/react/src/store/themeStore.js b/packages/react/src/store/themeStore.js index 72d5cd18a1..4da0305089 100644 --- a/packages/react/src/store/themeStore.js +++ b/packages/react/src/store/themeStore.js @@ -1,9 +1,9 @@ import { create } from 'zustand'; const useThemeStore = create((set) => ({ - isDarkMode: false, - setIsDarkMode: (isDarkMode) => set(() => ({ isDarkMode })), - toggleTheme: () => set((state) => ({ isDarkMode: !state.isDarkMode })), + isDarkMode: false, + setIsDarkMode: (isDarkMode) => set(() => ({ isDarkMode })), + toggleTheme: () => set((state) => ({ isDarkMode: !state.isDarkMode })), })); export default useThemeStore; diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 7f61d41397..2ffbb9945e 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -405,8 +405,8 @@ const ChatHeader = ({ isRoomTeam ? 'team' : isChannelPrivate - ? 'hash_lock' - : 'hash' + ? 'hash_lock' + : 'hash' } size={fullScreen ? '1.25rem' : '1rem'} /> diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index 81f7dca69d..690b771aa3 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -18,7 +18,12 @@ import { import { ChatLayout } from './ChatLayout'; import { ChatHeader } from './ChatHeader'; import { RCInstanceProvider } from '../context/RCInstance'; -import { useUserStore, useLoginStore, useMessageStore, useThemeStore } from '../store'; +import { + useUserStore, + useLoginStore, + useMessageStore, + useThemeStore, +} from '../store'; import DefaultTheme from '../theme/DefaultTheme'; import { getTokenStorage } from '../lib/auth'; import { styles } from './EmbeddedChat.styles'; @@ -34,7 +39,7 @@ const EmbeddedChat = (props) => { const { isClosable = false, - setClosableState = () => { }, + setClosableState = () => {}, width = '100%', height = '95vh', host = 'http://localhost:3000', @@ -234,7 +239,10 @@ const EmbeddedChat = (props) => { if (!isSynced) return null; return ( - + ( - - - + + + ); export default Moon; diff --git a/packages/ui-elements/src/components/Icon/icons/Sun.js b/packages/ui-elements/src/components/Icon/icons/Sun.js index 6d340b64a5..a5210afcd5 100644 --- a/packages/ui-elements/src/components/Icon/icons/Sun.js +++ b/packages/ui-elements/src/components/Icon/icons/Sun.js @@ -1,14 +1,14 @@ import React from 'react'; const Sun = (props) => ( - - - + + + ); export default Sun;