diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index 0a4bc1e910..32f8352f23 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -1,12 +1,4 @@ -import { - createContext, - type PropsWithChildren, - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { ChannelFilters, ChannelOptions, @@ -35,14 +27,14 @@ import { MessageList, Window, WithComponents, - GroupAvatar, ReactionsList, - useMessageContext, } from 'stream-chat-react'; import { createTextComposerEmojiMiddleware, EmojiPicker } from 'stream-chat-react/emojis'; import { init, SearchIndex } from 'emoji-mart'; import data from '@emoji-mart/data'; import { humanId } from 'human-id'; +import { chatViewSelectorItemSet } from './Sidebar/ChatViewSelectorItemSet.tsx'; +import { useAppSettingsState } from './AppSettings'; init({ data }); @@ -56,7 +48,8 @@ if (!apiKey) { } const options: ChannelOptions = { limit: 5, presence: true, state: true }; -const sort: ChannelSort = { pinned_at: 1, last_message_at: -1, updated_at: -1 }; +// pinned_at param leads to BE not returning (empty) channels +const sort: ChannelSort = { last_message_at: -1, updated_at: -1 }; // @ts-ignore const isMessageAIGenerated = (message: LocalMessage) => !!message?.ai_generated; @@ -93,7 +86,8 @@ const useUser = () => { }; const CustomMessageReactions = (props: React.ComponentProps) => { - const { visualStyle, verticalPosition, flipHorizontalPosition } = useContext(TempCtx); + const { reactions } = useAppSettingsState(); + const { visualStyle, verticalPosition, flipHorizontalPosition } = reactions; return ( ({ - visualStyle: 'clustered', - verticalPosition: 'top', - flipHorizontalPosition: false, -}); - const App = () => { const { userId, tokenProvider } = useUser(); - const [tempCtxValue, setTempCtxValue] = useState({ - visualStyle: 'clustered', - verticalPosition: 'top', - flipHorizontalPosition: false, - }); - const chatClient = useCreateChatClient({ apiKey, tokenOrProvider: tokenProvider, @@ -133,9 +110,12 @@ const App = () => { const filters: ChannelFilters = useMemo( () => ({ - members: { $in: [userId] }, - type: 'messaging', - archived: false, + $or: [ + { + members: { $in: [userId] }, + }, + { type: 'public' }, + ], }), [userId], ); @@ -184,85 +164,49 @@ const App = () => { if (!chatClient) return <>Loading...; return ( - - - - - - - - - -
- - - -
- - - - -
- -
-
- - - - - - -
-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + ); }; diff --git a/examples/vite/src/AppSettings/AppSettings.scss b/examples/vite/src/AppSettings/AppSettings.scss new file mode 100644 index 0000000000..963215f0ee --- /dev/null +++ b/examples/vite/src/AppSettings/AppSettings.scss @@ -0,0 +1,150 @@ +@layer stream-app-overrides { + .app__settings-group { + height: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; + gap: 16px; + + .app__settings-group_button { + color: var(--text-secondary); + + svg { + height: 2rem; + width: 2rem; + } + } + } + + .app__settings-modal { + display: flex; + flex-direction: column; + width: min(920px, 90vw); + max-height: min(80vh, 760px); + min-height: min(520px, 72vh); + background: #fff; + border-radius: 14px; + } + + .app__settings-modal__header { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 16px 20px; + font-size: 1.5rem; + font-weight: 700; + border-bottom: 1px solid #dfe5ef; + + svg.str-chat__icon--cog { + height: 1.75rem; + width: 1.75rem; + } + } + + .app__settings-modal__body { + display: grid; + grid-template-columns: minmax(180px, 240px) minmax(0, 1fr); + min-height: 0; + height: 100%; + } + + .app__settings-modal__tabs { + overflow-y: auto; + border-right: 1px solid #dfe5ef; + padding: 10px; + } + + .app__settings-modal__tab { + width: 100%; + white-space: nowrap; + justify-content: flex-start; + font-weight: 500; + margin-bottom: 6px; + } + + .app__settings-modal__tab[aria-selected='true'], + .app__settings-modal__tab.app__settings-modal__tab--active { + background: #e9f0ff; + border-color: #3167f6; + font-weight: 600; + } + + .app__settings-modal__content { + overflow-y: auto; + padding: 20px 24px; + } + + .app__settings-modal__content-stack { + display: flex; + flex-direction: column; + gap: 20px; + } + + .app__settings-modal__field { + display: flex; + flex-direction: column; + gap: 10px; + } + + .app__settings-modal__field-label { + font-weight: 600; + color: #2f3550; + } + + .app__settings-modal__options-row { + display: flex; + gap: 10px; + flex-wrap: wrap; + } + + .app__settings-modal__option-button[aria-pressed='true'] { + border-color: #3167f6; + background: #e9f0ff; + font-weight: 600; + } + + .app__settings-modal__checkbox-label { + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + } + + .app__settings-modal__preview { + border: 1px solid var(--border); + border-radius: 12px; + padding: 12px; + background: var(--bg-surface); + + .str-chat__li--single { + list-style: none; + margin: 0; + padding: 0; + } + + .str-chat__message-options, + .str-chat__message-actions-box-button { + display: none; + } + } + + @media (max-width: 760px) { + .app__settings-modal { + width: min(92vw, 680px); + } + + .app__settings-modal__body { + grid-template-columns: 1fr; + } + + .app__settings-modal__tabs { + border-right: 0; + border-bottom: 1px solid #dfe5ef; + display: flex; + gap: 8px; + padding: 10px 12px; + overflow-x: auto; + overflow-y: hidden; + } + } +} diff --git a/examples/vite/src/AppSettings/AppSettings.tsx b/examples/vite/src/AppSettings/AppSettings.tsx new file mode 100644 index 0000000000..39b163b6ea --- /dev/null +++ b/examples/vite/src/AppSettings/AppSettings.tsx @@ -0,0 +1,68 @@ +import { + Button, + ChatViewSelectorButton, + GlobalModal, + IconCog, + IconEmoji, +} from 'stream-chat-react'; +import { type ComponentType, useState } from 'react'; +import { ReactionsTab } from './tabs/Reactions'; + +type TabId = 'reactions'; + +const tabConfig: { Icon: ComponentType; id: TabId; title: string }[] = [ + { Icon: IconEmoji, id: 'reactions', title: 'Reactions' }, +]; + +export const AppSettings = () => { + const [activeTab, setActiveTab] = useState('reactions'); + const [open, setOpen] = useState(false); + + return ( +
+ setOpen(true)} + text='Settings' + /> + setOpen(false)}> +
+
+ + Settings +
+
+ +
+ {activeTab === 'reactions' && } +
+
+
+
+
+ ); +}; diff --git a/examples/vite/src/AppSettings/index.ts b/examples/vite/src/AppSettings/index.ts new file mode 100644 index 0000000000..5126fea671 --- /dev/null +++ b/examples/vite/src/AppSettings/index.ts @@ -0,0 +1,2 @@ +export * from './AppSettings'; +export * from './state'; diff --git a/examples/vite/src/AppSettings/state.ts b/examples/vite/src/AppSettings/state.ts new file mode 100644 index 0000000000..01971c3165 --- /dev/null +++ b/examples/vite/src/AppSettings/state.ts @@ -0,0 +1,26 @@ +import { StateStore } from 'stream-chat'; +import { useStateStore } from 'stream-chat-react'; + +export type ReactionsSettingsState = { + flipHorizontalPosition: boolean; + verticalPosition: 'top' | 'bottom'; + visualStyle: 'clustered' | 'segmented'; +}; + +export type AppSettingsState = { + reactions: ReactionsSettingsState; +}; + +const defaultAppSettingsState: AppSettingsState = { + reactions: { + flipHorizontalPosition: false, + verticalPosition: 'top', + visualStyle: 'clustered', + }, +}; + +export const appSettingsStore = new StateStore(defaultAppSettingsState); + +export const useAppSettingsState = () => + useStateStore(appSettingsStore, (nextValue: AppSettingsState) => nextValue) ?? + defaultAppSettingsState; diff --git a/examples/vite/src/AppSettings/tabs/Reactions/ReactionsTab.tsx b/examples/vite/src/AppSettings/tabs/Reactions/ReactionsTab.tsx new file mode 100644 index 0000000000..f572518964 --- /dev/null +++ b/examples/vite/src/AppSettings/tabs/Reactions/ReactionsTab.tsx @@ -0,0 +1,133 @@ +import { + Button, + ChannelActionProvider, + ChannelStateProvider, + ComponentProvider, + Message, + useComponentContext, +} from 'stream-chat-react'; +import { appSettingsStore, useAppSettingsState } from '../../state'; +import { + reactionsPreviewChannelActions, + reactionsPreviewChannelState, + reactionsPreviewMessage, + reactionsPreviewOptions, +} from './reactionsExampleData'; + +export const ReactionsTab = () => { + const state = useAppSettingsState(); + const { reactions } = state; + const componentContext = useComponentContext(); + + return ( +
+
+
Visual style
+
+ + +
+
+ +
+
Vertical position
+
+ + +
+
+ +
+
Horizontal alignment
+
+ + +
+
+ +
+
Preview
+
+ + + +
  • + +
  • +
    +
    +
    +
    +
    +
    + ); +}; diff --git a/examples/vite/src/AppSettings/tabs/Reactions/index.ts b/examples/vite/src/AppSettings/tabs/Reactions/index.ts new file mode 100644 index 0000000000..d003e2af00 --- /dev/null +++ b/examples/vite/src/AppSettings/tabs/Reactions/index.ts @@ -0,0 +1 @@ +export * from './ReactionsTab'; diff --git a/examples/vite/src/AppSettings/tabs/Reactions/reactionsExampleData.ts b/examples/vite/src/AppSettings/tabs/Reactions/reactionsExampleData.ts new file mode 100644 index 0000000000..836df32e7c --- /dev/null +++ b/examples/vite/src/AppSettings/tabs/Reactions/reactionsExampleData.ts @@ -0,0 +1,170 @@ +import type { LocalMessage } from 'stream-chat'; + +const fireReactionTimestamp = '2026-02-12T06:39:57.188362Z'; +const firstLikeReactionTimestamp = '2026-02-12T06:39:56.237389Z'; +const secondLikeReactionTimestamp = '2026-02-12T06:39:52.237389Z'; +const heartReactionTimestamp = '2026-02-12T06:35:58.021196Z'; + +export const reactionsPreviewMessage: LocalMessage = { + created_at: new Date('2026-02-12T06:34:40.000000Z'), + deleted_at: null, + id: 'settings-preview-message-id', + latest_reactions: [ + { + created_at: fireReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'fire', + updated_at: fireReactionTimestamp, + user: { + id: 'test-user', + language: '', + role: 'user', + teams: [], + }, + user_id: 'test-user', + }, + { + created_at: firstLikeReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'like', + updated_at: firstLikeReactionTimestamp, + user: { + id: 'test-user', + language: '', + role: 'user', + teams: [], + }, + user_id: 'test-user', + }, + { + created_at: secondLikeReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'like', + updated_at: secondLikeReactionTimestamp, + user: { + id: 'test-user-2', + language: '', + role: 'user', + teams: [], + }, + user_id: 'test-user-2', + }, + { + created_at: heartReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'heart', + updated_at: heartReactionTimestamp, + user: { id: 'test-user-2' }, + user_id: 'test-user-2', + }, + ] as LocalMessage['latest_reactions'], + own_reactions: [ + { + created_at: fireReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'fire', + updated_at: fireReactionTimestamp, + user: { id: 'test-user' }, + user_id: 'test-user', + }, + { + created_at: firstLikeReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'like', + updated_at: firstLikeReactionTimestamp, + user: { id: 'test-user' }, + user_id: 'test-user', + }, + { + created_at: heartReactionTimestamp, + message_id: 'settings-preview-message-id', + score: 1, + type: 'heart', + updated_at: heartReactionTimestamp, + user: { id: 'test-user' }, + user_id: 'test-user', + }, + ] as LocalMessage['own_reactions'], + pinned_at: null, + reaction_counts: { fire: 1, heart: 1, like: 2 }, + reaction_groups: { + fire: { + count: 1, + first_reaction_at: fireReactionTimestamp, + last_reaction_at: fireReactionTimestamp, + sum_scores: 1, + }, + heart: { + count: 1, + first_reaction_at: heartReactionTimestamp, + last_reaction_at: heartReactionTimestamp, + sum_scores: 1, + }, + like: { + count: 2, + first_reaction_at: secondLikeReactionTimestamp, + last_reaction_at: firstLikeReactionTimestamp, + sum_scores: 2, + }, + } as LocalMessage['reaction_groups'], + reaction_scores: { fire: 1, heart: 1, like: 2 }, + status: 'received', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed lectus nibh, rutrum in risus eget, dictum commodo dolor. Donec augue nisi, sollicitudin sed magna ut, tincidunt pretium lorem. ', + type: 'regular', + updated_at: new Date('2026-02-12T06:40:00.000000Z'), + user: { + id: 'settings-preview-user', + image: 'https://getstream.io/random_svg/?id=preview-user&name=Preview+User', + name: 'Preview User', + }, +}; + +export const reactionsPreviewChannelState = { + channel: { + state: { + membership: { + channel_role: 'channel_member', + is_moderator: false, + role: 'member', + }, + }, + }, + channelCapabilities: {}, + channelConfig: undefined, + imageAttachmentSizeHandler: () => ({ url: '' }), + notifications: [], + shouldGenerateVideoThumbnail: false, + videoAttachmentSizeHandler: () => ({ url: '' }), +}; + +export const reactionsPreviewChannelActions = { + addNotification: () => undefined, + closeThread: () => undefined, + onMentionsClick: () => undefined, + onMentionsHover: () => undefined, + openThread: () => undefined, +}; + +export const reactionsPreviewOptions = [ + { + Component: () => '🔥', + name: 'Fire', + type: 'fire', + }, + { + Component: () => '👍', + name: 'Thumbs up', + type: 'like', + }, + { + Component: () => '❤️', + name: 'Heart', + type: 'heart', + }, +]; diff --git a/examples/vite/src/Sidebar/ChatViewSelectorItemSet.tsx b/examples/vite/src/Sidebar/ChatViewSelectorItemSet.tsx new file mode 100644 index 0000000000..c28cc05392 --- /dev/null +++ b/examples/vite/src/Sidebar/ChatViewSelectorItemSet.tsx @@ -0,0 +1,10 @@ +import { + type ChatViewSelectorEntry, + defaultChatViewSelectorItemSet, +} from 'stream-chat-react'; +import { AppSettings } from '../AppSettings'; + +export const chatViewSelectorItemSet: ChatViewSelectorEntry[] = [ + ...defaultChatViewSelectorItemSet, + { Component: AppSettings, type: 'settings' }, +]; diff --git a/examples/vite/src/index.scss b/examples/vite/src/index.scss index 9636c26853..e5ad46e401 100644 --- a/examples/vite/src/index.scss +++ b/examples/vite/src/index.scss @@ -1,4 +1,4 @@ -@layer stream, stream-new, stream-overrides; +@layer stream, stream-new, stream-overrides, stream-app-overrides; @import url('./stream-imports-theme.scss') layer(stream); @import url('./stream-imports-layout.scss') layer(stream); @@ -6,6 +6,7 @@ // v3 CSS import @import url('stream-chat-react/dist/css/index.css') layer(stream-new); @import url('stream-chat-react/dist/css/emojis.css') layer(stream-new); +@import url('./AppSettings/AppSettings.scss') layer(stream-app-overrides); :root { font-synthesis: none; @@ -109,7 +110,7 @@ body { } } - @container (max-width: 1025px) { + @container (max-width: 760px) { .str-chat__channel-list, .str-chat__chat-view__selector { display: none; diff --git a/examples/vite/src/stream-imports-layout.scss b/examples/vite/src/stream-imports-layout.scss index 3868f2a722..84456cc381 100644 --- a/examples/vite/src/stream-imports-layout.scss +++ b/examples/vite/src/stream-imports-layout.scss @@ -44,6 +44,6 @@ @use 'stream-chat-react/dist/scss/v2/Tooltip/Tooltip-layout'; @use 'stream-chat-react/dist/scss/v2/TypingIndicator/TypingIndicator-layout'; @use 'stream-chat-react/dist/scss/v2/ThreadList/ThreadList-layout'; -@use 'stream-chat-react/dist/scss/v2/ChatView/ChatView-layout'; +//@use 'stream-chat-react/dist/scss/v2/ChatView/ChatView-layout'; @use 'stream-chat-react/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-layout'; @use 'stream-chat-react/dist/scss/v2/AIStateIndicator/AIStateIndicator-layout'; diff --git a/examples/vite/src/stream-imports-theme.scss b/examples/vite/src/stream-imports-theme.scss index e917177908..727c02e92b 100644 --- a/examples/vite/src/stream-imports-theme.scss +++ b/examples/vite/src/stream-imports-theme.scss @@ -38,6 +38,6 @@ @use 'stream-chat-react/dist/scss/v2/Tooltip/Tooltip-theme'; @use 'stream-chat-react/dist/scss/v2/TypingIndicator/TypingIndicator-theme'; @use 'stream-chat-react/dist/scss/v2/ThreadList/ThreadList-theme'; -@use 'stream-chat-react/dist/scss/v2/ChatView/ChatView-theme'; +//@use 'stream-chat-react/dist/scss/v2/ChatView/ChatView-theme'; @use 'stream-chat-react/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-theme'; @use 'stream-chat-react/dist/scss/v2/AIStateIndicator/AIStateIndicator-theme'; diff --git a/src/components/Attachment/styling/ModalGallery.scss b/src/components/Attachment/styling/ModalGallery.scss index 8172f4f3ae..6ebd665a94 100644 --- a/src/components/Attachment/styling/ModalGallery.scss +++ b/src/components/Attachment/styling/ModalGallery.scss @@ -39,8 +39,7 @@ } } - .str-chat__modal-gallery-placeholder { - position: relative; + .str-chat__modal-gallery__placeholder { display: flex; align-items: center; justify-content: center; @@ -57,21 +56,9 @@ font-weight: var(--typography-font-weight-medium); line-height: var(--typography-line-height-relaxed); - p { - position: relative; - z-index: 1; - } - - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - background-color: var(--background-core-scrim); - } + inset: 0; + position: absolute; + background-color: var(--background-core-scrim); } } } @@ -110,4 +97,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/components/ChatView/ChatView.tsx b/src/components/ChatView/ChatView.tsx index 014731c87c..51053d7126 100644 --- a/src/components/ChatView/ChatView.tsx +++ b/src/components/ChatView/ChatView.tsx @@ -1,20 +1,28 @@ -import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; - +import clsx from 'clsx'; +import React, { + type ComponentType, + createContext, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; + +import { Button, type ButtonProps } from '../Button'; import { ThreadProvider } from '../Threads'; import { Icon } from '../Threads/icons'; import { UnreadCountBadge } from '../Threads/UnreadCountBadge'; -import { useChatContext } from '../../context'; +import { useChatContext, useTranslationContext } from '../../context'; import { useStateStore } from '../../store'; import type { PropsWithChildren } from 'react'; import type { Thread, ThreadManagerState } from 'stream-chat'; -import clsx from 'clsx'; -type ChatView = 'channels' | 'threads'; +export type ChatView = 'channels' | 'threads'; type ChatViewContextValue = { activeChatView: ChatView; - setActiveChatView: (cv: ChatViewContextValue['activeChatView']) => void; + setActiveChatView: (cv: ChatView) => void; }; const ChatViewContext = createContext({ @@ -22,9 +30,10 @@ const ChatViewContext = createContext({ setActiveChatView: () => undefined, }); +export const useChatViewContext = () => useContext(ChatViewContext); + export const ChatView = ({ children }: PropsWithChildren) => { - const [activeChatView, setActiveChatView] = - useState('channels'); + const [activeChatView, setActiveChatView] = useState('channels'); const { theme } = useChatContext(); @@ -38,7 +47,7 @@ export const ChatView = ({ children }: PropsWithChildren) => { }; const ChannelsView = ({ children }: PropsWithChildren) => { - const { activeChatView } = useContext(ChatViewContext); + const { activeChatView } = useChatViewContext(); if (activeChatView !== 'channels') return null; @@ -58,7 +67,7 @@ const ThreadsViewContext = createContext({ export const useThreadsViewContext = () => useContext(ThreadsViewContext); const ThreadsView = ({ children }: PropsWithChildren) => { - const { activeChatView } = useContext(ChatViewContext); + const { activeChatView } = useChatViewContext(); const [activeThread, setActiveThread] = useState(undefined); @@ -125,42 +134,105 @@ const ThreadAdapter = ({ children }: PropsWithChildren) => { return {children}; }; +export const ChatViewSelectorButton = ({ + children, + className, + Icon, + text, + ...props +}: ButtonProps & { Icon?: ComponentType; text?: string }) => ( + +); + const selector = ({ unreadThreadCount }: ThreadManagerState) => ({ unreadThreadCount, }); -const ChatViewSelector = () => { - const { client } = useChatContext(); - const { unreadThreadCount } = useStateStore(client.threads.state, selector); +export const ChatViewChannelsSelectorButton = () => { + const { activeChatView, setActiveChatView } = useChatViewContext(); + const { t } = useTranslationContext(); - const { activeChatView, setActiveChatView } = useContext(ChatViewContext); + return ( + setActiveChatView('channels')} + text={t('Channels')} + /> + ); +}; + +export const ChatViewThreadsSelectorButton = () => { + const { client } = useChatContext(); + const { unreadThreadCount } = useStateStore(client.threads.state, selector) ?? { + unreadThreadCount: 0, + }; + const { activeChatView, setActiveChatView } = useChatViewContext(); + const { t } = useTranslationContext(); return ( -
    - - -
    + setActiveChatView('threads')} + > + + + +
    {t('Threads')}
    +
    ); }; +export type ChatViewSelectorItem = { + Component: React.ComponentType; + type: string & {}; +}; + +export type ChatViewSelectorEntry = ChatViewSelectorItem; + +export type ChatViewSelectorProps = { + itemSet?: ChatViewSelectorEntry[]; +}; + +export const defaultChatViewSelectorItemSet: ChatViewSelectorEntry[] = [ + { + Component: ChatViewChannelsSelectorButton, + type: 'channels' as string & {}, + }, + { + Component: ChatViewThreadsSelectorButton, + type: 'threads' as string & {}, + }, +]; + +const ChatViewSelector = ({ + itemSet = defaultChatViewSelectorItemSet, +}: ChatViewSelectorProps) => ( +
    + {itemSet.map(({ Component, type }) => ( + + ))} +
    +); + ChatView.Channels = ChannelsView; ChatView.Threads = ThreadsView; ChatView.ThreadAdapter = ThreadAdapter; diff --git a/src/components/ChatView/styling/ChatView.scss b/src/components/ChatView/styling/ChatView.scss new file mode 100644 index 0000000000..3cfd31ef93 --- /dev/null +++ b/src/components/ChatView/styling/ChatView.scss @@ -0,0 +1,65 @@ +.str-chat { + --str-chat-selector-background-color: var(--str-chat__secondary-background-color); + --str-chat-selector-border-color: var(--str-chat__surface-color); + + --str-chat-selector-button-color-default: var(--str-chat__text-low-emphasis-color); + --str-chat-selector-button-color-selected: var(--str-chat__text-color); + --str-chat-selector-button-background-color-default: transparent; + --str-chat-selector-button-background-color-selected: var(--str-chat__surface-color); +} + +.str-chat__chat-view { + display: flex; + width: 100%; + height: 100%; + + .str-chat__chat-view__selector { + display: flex; + flex-direction: column; + padding-inline: 8px; + padding-block: 16px; + gap: 20px; + border-right: 1px solid var(--str-chat-selector-border-color); + background-color: var(--str-chat-selector-background-color); + + .str-chat__chat-view__selector-button { + --str-chat-icon-height: 20px; + --str-chat-icon-width: 20px; + --str-chat-unread-count-badge-absolute-offset-vertical: 25%; + --str-chat-icon-color: var(--str-chat-selector-button-color-default); + + flex-direction: column; + gap: 4px; + padding-inline: 10px; + padding-block: 16px; + border-radius: 8px; + font-size: 12px; + line-height: 1; + position: relative; + + background: var(--str-chat-selector-button-background-color-default); + color: var(--str-chat-selector-button-color-default); + + &[aria-selected='true'] { + --str-chat-icon-color: var(--str-chat-selector-button-color-selected); + color: var(--str-chat-selector-button-color-selected); + background: var(--str-chat-selector-button-background-color-selected); + } + + svg { + height: var(--str-chat-icon-height); + width: var(--str-chat-icon-height); + } + } + } + + .str-chat__chat-view__channels { + display: flex; + flex-grow: 1; + } + + .str-chat__chat-view__threads { + display: flex; + flex-grow: 1; + } +} diff --git a/src/components/ChatView/styling/index.scss b/src/components/ChatView/styling/index.scss new file mode 100644 index 0000000000..a90bfa8923 --- /dev/null +++ b/src/components/ChatView/styling/index.scss @@ -0,0 +1 @@ +@use 'ChatView'; diff --git a/src/components/DateSeparator/styling/DateSeparator.scss b/src/components/DateSeparator/styling/DateSeparator.scss index 72834ac3cd..e242b6f762 100644 --- a/src/components/DateSeparator/styling/DateSeparator.scss +++ b/src/components/DateSeparator/styling/DateSeparator.scss @@ -20,7 +20,6 @@ width: 100%; padding: var(--spacing-xs) 0; - .str-chat__date-separator-date { display: flex; padding: var(--spacing-xxs) var(--spacing-sm); diff --git a/src/components/DateSeparator/styling/index.scss b/src/components/DateSeparator/styling/index.scss index fcc3033fa8..7dfdc24840 100644 --- a/src/components/DateSeparator/styling/index.scss +++ b/src/components/DateSeparator/styling/index.scss @@ -1 +1 @@ -@use 'DateSeparator'; \ No newline at end of file +@use 'DateSeparator'; diff --git a/src/components/Gallery/styling/index.scss b/src/components/Gallery/styling/index.scss index 40df39d161..96de974cfd 100644 --- a/src/components/Gallery/styling/index.scss +++ b/src/components/Gallery/styling/index.scss @@ -1 +1 @@ -@use 'Gallery'; \ No newline at end of file +@use 'Gallery'; diff --git a/src/components/Icons/IconCog.tsx b/src/components/Icons/IconCog.tsx new file mode 100644 index 0000000000..e9b548f2a8 --- /dev/null +++ b/src/components/Icons/IconCog.tsx @@ -0,0 +1,13 @@ +import { BaseIcon } from './BaseIcon'; +import type { ComponentProps } from 'react'; +import clsx from 'clsx'; + +export const IconCog = ({ className, ...props }: ComponentProps<'svg'>) => ( + + + +); diff --git a/src/components/Icons/IconCross.tsx b/src/components/Icons/IconCross.tsx index 715131d91e..523588587b 100644 --- a/src/components/Icons/IconCross.tsx +++ b/src/components/Icons/IconCross.tsx @@ -7,7 +7,6 @@ export const IconCross = ({ className, ...props }: ComponentProps<'svg'>) => ( {...props} className={clsx('str-chat__icon--cross', className)} viewBox='0 0 10 10' - xmlns='http://www.w3.org/2000/svg' > diff --git a/src/components/Icons/IconEmoji.tsx b/src/components/Icons/IconEmoji.tsx new file mode 100644 index 0000000000..55b866265a --- /dev/null +++ b/src/components/Icons/IconEmoji.tsx @@ -0,0 +1,16 @@ +import { BaseIcon } from './BaseIcon'; +import type { ComponentProps } from 'react'; +import clsx from 'clsx'; + +export const IconEmoji = ({ className, ...props }: ComponentProps<'svg'>) => ( + + + + + + +); diff --git a/src/components/Icons/index.ts b/src/components/Icons/index.ts index adf4e9b2bb..9b1da8717d 100644 --- a/src/components/Icons/index.ts +++ b/src/components/Icons/index.ts @@ -5,9 +5,11 @@ export * from './IconChainLink'; export * from './IconCheckmark'; export * from './IconChevronRight'; export * from './IconClose'; +export * from './IconCog'; export * from './IconCommand'; export * from './IconCross'; export * from './IconDoubleCheckmark'; +export * from './IconEmoji'; export * from './IconExclamationCircle'; export * from './IconExclamationTriangle'; export * from './IconEyeOpen'; diff --git a/src/components/Icons/styling/IconCog.scss b/src/components/Icons/styling/IconCog.scss new file mode 100644 index 0000000000..e093d88996 --- /dev/null +++ b/src/components/Icons/styling/IconCog.scss @@ -0,0 +1,9 @@ +.str-chat__icon--cog { + fill: none; + height: 16px; + width: 16px; + + path { + fill: currentColor; + } +} diff --git a/src/components/Icons/styling/IconDoubleCheckmark.scss b/src/components/Icons/styling/IconDoubleCheckmark.scss index 95b0573432..a096747a8a 100644 --- a/src/components/Icons/styling/IconDoubleCheckmark.scss +++ b/src/components/Icons/styling/IconDoubleCheckmark.scss @@ -11,4 +11,4 @@ stroke-width: 1.5; } } -} \ No newline at end of file +} diff --git a/src/components/Icons/styling/IconEmoji.scss b/src/components/Icons/styling/IconEmoji.scss new file mode 100644 index 0000000000..8ac07e2faf --- /dev/null +++ b/src/components/Icons/styling/IconEmoji.scss @@ -0,0 +1,11 @@ +.str-chat { + .str-chat__icon--emoji { + fill: none; + height: 16px; + width: 16px; + + path { + fill: currentColor; + } + } +} diff --git a/src/components/Icons/styling/IconSingleCheckmark.scss b/src/components/Icons/styling/IconSingleCheckmark.scss index f389203907..0c38bbb246 100644 --- a/src/components/Icons/styling/IconSingleCheckmark.scss +++ b/src/components/Icons/styling/IconSingleCheckmark.scss @@ -8,4 +8,4 @@ fill: currentColor; } } -} \ No newline at end of file +} diff --git a/src/components/Icons/styling/index.scss b/src/components/Icons/styling/index.scss index a6b1b5cb62..6f49ddeb85 100644 --- a/src/components/Icons/styling/index.scss +++ b/src/components/Icons/styling/index.scss @@ -6,9 +6,11 @@ @use 'IconCheckmark'; @use 'IconChevronRight'; @use 'IconClose'; +@use 'IconCog'; @use 'IconCommand'; @use 'IconCross'; @use 'IconDoubleCheckmark'; +@use 'IconEmoji'; @use 'IconExclamationCircle'; @use 'IconExclamationTriangle'; @use 'IconEyeOpen'; diff --git a/src/components/Message/styling/Message.scss b/src/components/Message/styling/Message.scss index b422386f81..c8c20f65e2 100644 --- a/src/components/Message/styling/Message.scss +++ b/src/components/Message/styling/Message.scss @@ -388,6 +388,7 @@ .str-chat__message-simple-name { @include utils.prevent-glitch-text-overflow; + margin-inline-end: var(--spacing-xxs); } .str-chat__message-sender-name { diff --git a/src/components/Modal/GlobalModal.tsx b/src/components/Modal/GlobalModal.tsx index 5d75302278..9b43e34f5a 100644 --- a/src/components/Modal/GlobalModal.tsx +++ b/src/components/Modal/GlobalModal.tsx @@ -53,7 +53,7 @@ export const GlobalModal = ({ }; useEffect(() => { - if (!isOpen) return; + if (!open || !isOpen) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') maybeClose('escape', event); @@ -61,13 +61,13 @@ export const GlobalModal = ({ document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); - }, [isOpen, maybeClose]); + }, [isOpen, maybeClose, open]); useEffect(() => { - if (open && !dialog.isOpen) { + if (open && !isOpen) { dialog.open(); } - }, [dialog, open]); + }, [dialog, isOpen, open]); if (!open || !isOpen) return null; diff --git a/src/components/Modal/styling/Modal.scss b/src/components/Modal/styling/Modal.scss index 892b3cd08d..ae46f68655 100644 --- a/src/components/Modal/styling/Modal.scss +++ b/src/components/Modal/styling/Modal.scss @@ -144,4 +144,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/components/Modal/styling/index.scss b/src/components/Modal/styling/index.scss index 21d1deb317..d0efde238b 100644 --- a/src/components/Modal/styling/index.scss +++ b/src/components/Modal/styling/index.scss @@ -1 +1 @@ -@use 'Modal'; \ No newline at end of file +@use 'Modal'; diff --git a/src/components/Reactions/styling/ReactionList.scss b/src/components/Reactions/styling/ReactionList.scss index ba30a428ca..c4b82ca924 100644 --- a/src/components/Reactions/styling/ReactionList.scss +++ b/src/components/Reactions/styling/ReactionList.scss @@ -13,7 +13,6 @@ font-weight: var(--typography-font-weight-bold); line-height: 1; - .str-chat__message-reactions__list { list-style: none; margin: 0; diff --git a/src/components/VideoPlayer/styling/VideoThumbnail.scss b/src/components/VideoPlayer/styling/VideoThumbnail.scss index fd6092c16e..bf21d2e646 100644 --- a/src/components/VideoPlayer/styling/VideoThumbnail.scss +++ b/src/components/VideoPlayer/styling/VideoThumbnail.scss @@ -34,4 +34,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/components/VideoPlayer/styling/index.scss b/src/components/VideoPlayer/styling/index.scss index ecf1b0f340..366a25a95d 100644 --- a/src/components/VideoPlayer/styling/index.scss +++ b/src/components/VideoPlayer/styling/index.scss @@ -1 +1 @@ -@use "VideoThumbnail"; \ No newline at end of file +@use 'VideoThumbnail'; diff --git a/src/i18n/de.json b/src/i18n/de.json index 2ad648346b..5da8de3460 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -79,6 +79,7 @@ "Cancel": "Abbrechen", "Cannot seek in the recording": "In der Aufnahme kann nicht gesucht werden", "Channel Missing": "Kanal fehlt", + "Channels": "Kanäle", "Close": "Schließen", "Close emoji picker": "Emoji-Auswahl schließen", "Commands": "Befehle", @@ -245,6 +246,7 @@ "Thread": "Thread", "Thread has not been found": "Thread wurde nicht gefunden", "Thread reply": "Thread-Antwort", + "Threads": "Diskussionen", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/en.json b/src/i18n/en.json index 153b319eaf..97b4b28c57 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -79,6 +79,7 @@ "Cancel": "Cancel", "Cannot seek in the recording": "Cannot seek in the recording", "Channel Missing": "Channel Missing", + "Channels": "Channels", "Close": "Close", "Close emoji picker": "Close emoji picker", "Commands": "Commands", @@ -245,6 +246,7 @@ "Thread": "Thread", "Thread has not been found": "Thread has not been found", "Thread reply": "Thread reply", + "Threads": "Threads", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/es.json b/src/i18n/es.json index ba5d77b61c..7281a54896 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -84,6 +84,7 @@ "Cancel": "Cancelar", "Cannot seek in the recording": "No se puede buscar en la grabación", "Channel Missing": "Falta canal", + "Channels": "Canales", "Close": "Cerrar", "Close emoji picker": "Cerrar el selector de emojis", "Commands": "Comandos", @@ -254,6 +255,7 @@ "Thread": "Hilo", "Thread has not been found": "No se ha encontrado el hilo", "Thread reply": "Respuesta en hilo", + "Threads": "Hilos", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index cdec2052b6..06630dc5ec 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -84,6 +84,7 @@ "Cancel": "Annuler", "Cannot seek in the recording": "Impossible de rechercher dans l'enregistrement", "Channel Missing": "Canal Manquant", + "Channels": "Canaux", "Close": "Fermer", "Close emoji picker": "Fermer le sélecteur d'émojis", "Commands": "Commandes", @@ -254,6 +255,7 @@ "Thread": "Fil de discussion", "Thread has not been found": "Le fil de discussion n'a pas été trouvé", "Thread reply": "Réponse dans le fil", + "Threads": "Fils", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/hi.json b/src/i18n/hi.json index 50d5ebbda7..b8d732dae2 100644 --- a/src/i18n/hi.json +++ b/src/i18n/hi.json @@ -79,6 +79,7 @@ "Cancel": "रद्द करें", "Cannot seek in the recording": "रेकॉर्डिंग में खोज नहीं की जा सकती", "Channel Missing": "चैनल उपलब्ध नहीं है", + "Channels": "चैनल", "Close": "बंद करे", "Close emoji picker": "इमोजी पिकर बंद करें", "Commands": "कमांड", @@ -246,6 +247,7 @@ "Thread": "रिप्लाई थ्रेड", "Thread has not been found": "थ्रेड नहीं मिला", "Thread reply": "थ्रेड में उत्तर", + "Threads": "थ्रेड्स", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/it.json b/src/i18n/it.json index 5995341e56..ad9c70f9f7 100644 --- a/src/i18n/it.json +++ b/src/i18n/it.json @@ -84,6 +84,7 @@ "Cancel": "Annulla", "Cannot seek in the recording": "Impossibile cercare nella registrazione", "Channel Missing": "Il canale non esiste", + "Channels": "Canali", "Close": "Chiudi", "Close emoji picker": "Chiudi il selettore di emoji", "Commands": "Comandi", @@ -254,6 +255,7 @@ "Thread": "Discussione", "Thread has not been found": "Discussione non trovata", "Thread reply": "Risposta nella discussione", + "Threads": "Thread", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/ja.json b/src/i18n/ja.json index a5b73a7b71..9f4c4e58a4 100644 --- a/src/i18n/ja.json +++ b/src/i18n/ja.json @@ -79,6 +79,7 @@ "Cancel": "キャンセル", "Cannot seek in the recording": "録音中にシークできません", "Channel Missing": "チャネルがありません", + "Channels": "チャンネル", "Close": "閉める", "Close emoji picker": "絵文字ピッカーを閉める", "Commands": "コマンド", @@ -245,6 +246,7 @@ "Thread": "スレッド", "Thread has not been found": "スレッドが見つかりませんでした", "Thread reply": "スレッドの返信", + "Threads": "スレッド", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/ko.json b/src/i18n/ko.json index 9bfcc346a1..9e37a9b42a 100644 --- a/src/i18n/ko.json +++ b/src/i18n/ko.json @@ -79,6 +79,7 @@ "Cancel": "취소", "Cannot seek in the recording": "녹음에서 찾을 수 없습니다", "Channel Missing": "채널 누락", + "Channels": "채널", "Close": "닫기", "Close emoji picker": "이모티콘 선택기 닫기", "Commands": "명령어", @@ -245,6 +246,7 @@ "Thread": "스레드", "Thread has not been found": "스레드를 찾을 수 없습니다", "Thread reply": "스레드 답장", + "Threads": "스레드", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/nl.json b/src/i18n/nl.json index e6f6eb332a..3bf733df6f 100644 --- a/src/i18n/nl.json +++ b/src/i18n/nl.json @@ -79,6 +79,7 @@ "Cancel": "Annuleer", "Cannot seek in the recording": "Kan niet zoeken in de opname", "Channel Missing": "Kanaal niet gevonden", + "Channels": "Kanalen", "Close": "Sluit", "Close emoji picker": "Sluit de emoji-kiezer", "Commands": "Commando's", @@ -247,6 +248,7 @@ "Thread": "Draadje", "Thread has not been found": "Draadje niet gevonden", "Thread reply": "Draadje antwoord", + "Threads": "Discussies", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/pt.json b/src/i18n/pt.json index 63a1cbe752..ec268017f0 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -84,6 +84,7 @@ "Cancel": "Cancelar", "Cannot seek in the recording": "Não é possível buscar na gravação", "Channel Missing": "Canal ausente", + "Channels": "Canais", "Close": "Fechar", "Close emoji picker": "Fechar seletor de emoji", "Commands": "Comandos", @@ -254,6 +255,7 @@ "Thread": "Fio", "Thread has not been found": "Fio não encontrado", "Thread reply": "Resposta no fio", + "Threads": "Tópicos", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 87bfa009b6..38c76dee72 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -89,6 +89,7 @@ "Cancel": "Отмена", "Cannot seek in the recording": "Невозможно осуществить поиск в записи", "Channel Missing": "Канал не найден", + "Channels": "Каналы", "Close": "Закрыть", "Close emoji picker": "Закрыть окно выбора смайлов", "Commands": "Команды", @@ -263,6 +264,7 @@ "Thread": "Ветка", "Thread has not been found": "Ветка не найдена", "Thread reply": "Ответ в ветке", + "Threads": "Треды", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/i18n/tr.json b/src/i18n/tr.json index f40a285b2b..24c2de5beb 100644 --- a/src/i18n/tr.json +++ b/src/i18n/tr.json @@ -79,6 +79,7 @@ "Cancel": "İptal", "Cannot seek in the recording": "Kayıtta arama yapılamıyor", "Channel Missing": "Kanal bulunamıyor", + "Channels": "Kanallar", "Close": "Kapat", "Close emoji picker": "Emoji seçiciyi kapat", "Commands": "Komutlar", @@ -245,6 +246,7 @@ "Thread": "Konu", "Thread has not been found": "Konu bulunamadı", "Thread reply": "Konu yanıtı", + "Threads": "İleti dizileri", "timestamp/DateSeparator": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: { \"sameDay\": \"[Today]\", \"nextDay\": \"[Tomorrow]\", \"lastDay\": \"[Yesterday]\", \"nextWeek\": \"dddd\", \"lastWeek\": \"[Last] dddd\", \"sameElse\": \"ddd, D MMM\" }) }}", "timestamp/LiveLocation": "{{ timestamp | timestampFormatter(calendar: true) }}", "timestamp/MessageTimestamp": "{{ timestamp | timestampFormatter(calendar: true) }}", diff --git a/src/plugins/Emojis/EmojiPicker.tsx b/src/plugins/Emojis/EmojiPicker.tsx index 8ed412e1cf..fc2ef66d9b 100644 --- a/src/plugins/Emojis/EmojiPicker.tsx +++ b/src/plugins/Emojis/EmojiPicker.tsx @@ -1,10 +1,13 @@ import React, { useEffect, useState } from 'react'; import Picker from '@emoji-mart/react'; -import { EmojiPickerIcon } from './icons'; import { useMessageInputContext, useTranslationContext } from '../../context'; -import type { PopperLikePlacement } from '../../components'; -import { Button, useMessageComposer } from '../../components'; +import { + Button, + IconEmoji, + type PopperLikePlacement, + useMessageComposer, +} from '../../components'; import { usePopoverPosition } from '../../components/Dialog/hooks/usePopoverPosition'; import clsx from 'clsx'; import { useIsCooldownActive } from '../../components/MessageInput/hooks/useIsCooldownActive'; @@ -67,7 +70,7 @@ export const EmojiPicker = (props: EmojiPickerProps) => { const { buttonClassName, pickerContainerClassName, wrapperClassName } = classNames; - const { ButtonIconComponent = EmojiPickerIcon } = props; + const { ButtonIconComponent = IconEmoji } = props; useEffect(() => { if (!popperElement || !referenceElement) return; @@ -118,7 +121,7 @@ export const EmojiPicker = (props: EmojiPickerProps) => { aria-expanded={displayPicker} aria-label={t('aria/Emoji picker')} className={props.buttonClassName ?? buttonClassName} - disabled={!!isCooldownActive} + disabled={isCooldownActive} onClick={() => setDisplayPicker((cv) => !cv)} ref={setReferenceElement} type='button' diff --git a/src/plugins/Emojis/icons.tsx b/src/plugins/Emojis/icons.tsx deleted file mode 100644 index e30d12bef4..0000000000 --- a/src/plugins/Emojis/icons.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, { type ComponentProps } from 'react'; -import clsx from 'clsx'; - -export const EmojiPickerIcon = ({ className, ...props }: ComponentProps<'svg'>) => ( - - - - - - -); diff --git a/src/plugins/Emojis/index.ts b/src/plugins/Emojis/index.ts index 016578681c..17d0119d3c 100644 --- a/src/plugins/Emojis/index.ts +++ b/src/plugins/Emojis/index.ts @@ -1,3 +1,2 @@ export * from './EmojiPicker'; export * from './middleware'; -export { EmojiPickerIcon } from './icons'; diff --git a/src/styling/index.scss b/src/styling/index.scss index 15911223f1..7fb2622621 100644 --- a/src/styling/index.scss +++ b/src/styling/index.scss @@ -23,6 +23,7 @@ @use '../components/AudioPlayback/styling' as AudioPlayback; @use '../components/Avatar/styling/Avatar' as Avatar; @use '../components/Avatar/styling/GroupAvatar' as GroupAvatar; +@use '../components/ChatView/styling' as ChatView; @use '../components/DateSeparator/styling' as DateSeparator; @use '../components/Gallery/styling' as Gallery; @use '../components/MediaRecorder/AudioRecorder/styling' as AudioRecorder;