@ethora/chat-component 26.4.4 → 26.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -355,7 +355,10 @@ Below is a grouped reference for all `config` options.
355
355
  | `headerLogo` | `string \| React.ReactElement` | Custom logo in header. |
356
356
  | `headerMenu` | `() => void` | Custom menu handler. |
357
357
  | `headerChatMenu` | `() => void` | Custom room header menu handler. |
358
- | `colors` | `{ primary: string; secondary: string }` | Theme colors for component UI. |
358
+ | `colors` | `{ primary; secondary; icons? }` | Theme colours. `primary` also drives icon colours and the message sender name; set `colors.icons` to give icons (attach, mic, send, file, empty-state) a colour independent of `primary`. |
359
+ | `typography` | `TypographyConfig` | Font family + size for the chat UI: `fontFamily` (or just `googleFontsFamily`) is applied across all chat text incl. messages and sender names; `fontSize` (px) scales message text, sender names, timestamps, inputs, room names and badges. Plus loaders (`googleFontsUrl`/`fontFaces`) and weight tokens. |
360
+ | `fallbackScreens` | `{ noUser?; noConnection?; noRoom? }` | Replace the built-in screens (Ethora login form, "Connecting...", empty room state) with your own text or React nodes. A plain string is rendered as centered text. |
361
+ | `hiddenRooms` | `{ titles?: string[]; jids?: string[] }` | Hide specific rooms from the room list and unread counters (e.g. `{ titles: ['Main chat'] }` to suppress the auto-created default room). |
359
362
  | `roomListStyles` | `React.CSSProperties` | Styles for room list pane. |
360
363
  | `chatRoomStyles` | `React.CSSProperties` | Styles for chat pane. |
361
364
  | `noMessagesPlaceholder` | `React.ComponentType` | Replace empty-chat placeholder component (same render position as default placeholder). |
@@ -602,7 +605,7 @@ function PushPermissionButton() {
602
605
  | --- | --- | --- |
603
606
  | `Chat` | React component | Main chat component. |
604
607
  | `XmppProvider` | React provider | Provides XMPP client context for internal hooks/state. |
605
- | `useUnread` | hook | Returns unread counters. |
608
+ | `useUnread` | hook | Returns unread counters (`totalCount`, per-room maps) plus a `loading` flag that stays `true` until the room list has loaded at least once. |
606
609
  | `logoutService` | service | Programmatic logout utility. |
607
610
  | `useQRCodeChat` | hook | Handle QR-based room links. |
608
611
  | `handleQRChatId` | function | Parse/process QR chat ID from URL. |
@@ -616,11 +619,11 @@ Basic hook usage:
616
619
  import { useUnread, logoutService } from '@ethora/chat-component';
617
620
 
618
621
  function HeaderActions() {
619
- const { totalCount } = useUnread();
622
+ const { totalCount, loading } = useUnread();
620
623
 
621
624
  return (
622
625
  <div>
623
- <span>Unread: {totalCount}</span>
626
+ <span>{loading ? 'Loading…' : `Unread: ${totalCount}`}</span>
624
627
  <button onClick={() => logoutService.performLogout()}>Logout</button>
625
628
  </div>
626
629
  );
@@ -2,10 +2,10 @@ export declare const AddNewIcon: ({ color, ...props }: {
2
2
  [x: string]: any;
3
3
  color?: string;
4
4
  }) => import("react/jsx-runtime").JSX.Element;
5
- export declare const AttachIcon: (props: any) => import("react/jsx-runtime").JSX.Element;
6
- export declare const CloseIcon: (props: any) => import("react/jsx-runtime").JSX.Element;
7
- export declare const DownloadIcon: (props: any) => import("react/jsx-runtime").JSX.Element;
8
- export declare const RecordIcon: (props: any) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const AttachIcon: ({ color, ...props }: any) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const CloseIcon: ({ color, ...props }: any) => import("react/jsx-runtime").JSX.Element;
7
+ export declare const DownloadIcon: ({ color, ...props }: any) => import("react/jsx-runtime").JSX.Element;
8
+ export declare const RecordIcon: ({ color, ...props }: any) => import("react/jsx-runtime").JSX.Element;
9
9
  export declare const RemoveIcon: (props: any) => import("react/jsx-runtime").JSX.Element;
10
10
  export declare const SendIcon: ({ color, ...props }: {
11
11
  [x: string]: any;
@@ -22,18 +22,10 @@ export declare const SearchIcon: ({ color, ...props }: {
22
22
  [x: string]: any;
23
23
  color?: string;
24
24
  }) => import("react/jsx-runtime").JSX.Element;
25
- export declare const NoSelectedChatIcon: ({ color, ...props }: {
26
- [x: string]: any;
27
- color?: string;
28
- }) => import("react/jsx-runtime").JSX.Element;
29
25
  export declare const ReferalsIcon: ({ color, ...props }: {
30
26
  [x: string]: any;
31
27
  color?: string;
32
28
  }) => import("react/jsx-runtime").JSX.Element;
33
- export declare const NoMessages: ({ color, ...props }: {
34
- [x: string]: any;
35
- color?: string;
36
- }) => import("react/jsx-runtime").JSX.Element;
37
29
  export declare const ReportIcon: ({ color, ...props }: {
38
30
  [x: string]: any;
39
31
  color?: string;
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+ interface EmptyChatIllustrationProps extends React.SVGProps<SVGSVGElement> {
3
+ width?: number | string;
4
+ height?: number | string;
5
+ }
6
+ export declare const EmptyChatIllustration: React.FC<EmptyChatIllustrationProps>;
7
+ export default EmptyChatIllustration;
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+ interface NoMessagesIllustrationProps extends React.SVGProps<SVGSVGElement> {
3
+ width?: number | string;
4
+ height?: number | string;
5
+ }
6
+ export declare const NoMessagesIllustration: React.FC<NoMessagesIllustrationProps>;
7
+ export default NoMessagesIllustration;
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ interface FallbackScreenProps {
3
+ /** A plain string is rendered as centered text; any other node as-is. */
4
+ content: React.ReactNode;
5
+ style?: React.CSSProperties;
6
+ }
7
+ /**
8
+ * Host-provided replacement for the built-in "cannot show chat" states
9
+ * (no user session, lost connection, no rooms). See IConfig.fallbackScreens.
10
+ */
11
+ declare const FallbackScreen: React.FC<FallbackScreenProps>;
12
+ export default FallbackScreen;
@@ -0,0 +1,4 @@
1
+ import { TypographyConfig } from '../types/models/config.model';
2
+ export declare function applyTypography(cfg?: TypographyConfig): void;
3
+ /** Remove everything {@link applyTypography} injected. */
4
+ export declare function clearTypography(): void;
@@ -0,0 +1,9 @@
1
+ import { IConfig, IRoom } from '../types/types';
2
+ type RoomLike = Pick<IRoom, 'jid' | 'title' | 'name'>;
3
+ /**
4
+ * True when the room is listed in `config.hiddenRooms` (by JID or by
5
+ * case-insensitive title). Hidden rooms stay joined — they are only
6
+ * suppressed from the room list and the unread counters.
7
+ */
8
+ export declare const isRoomHidden: (room: RoomLike | null | undefined, config?: Pick<IConfig, "hiddenRooms"> | null) => boolean;
9
+ export {};
@@ -0,0 +1,23 @@
1
+ import { IConfig } from '../types/types';
2
+ /**
3
+ * Resolve the colour to use for the chat's icons.
4
+ *
5
+ * Priority: explicit `config.colors.icons` → `config.colors.primary` →
6
+ * the historical default (#0052CD). Lets a host theme every icon at once by
7
+ * setting `colors.primary`, while still allowing icons to be decoupled via
8
+ * `colors.icons`.
9
+ */
10
+ export declare const resolveIconColor: (config?: Pick<IConfig, "colors"> | null) => string;
11
+ /** CSS variable read by the chat's chrome icons as their default colour. */
12
+ export declare const ICON_COLOR_VAR = "--ethora-icon-color";
13
+ /**
14
+ * Publish the resolved icon colour on the `--ethora-icon-color` CSS variable.
15
+ * Chrome icons default their `fill`/`stroke` to `var(--ethora-icon-color, …)`,
16
+ * so setting this themes every neutral icon at once without touching call
17
+ * sites. When neither `colors.icons` nor `colors.primary` is set, the variable
18
+ * is removed and each icon falls back to its own literal default.
19
+ *
20
+ * Semantic icons (destructive red, status ticks, brand) intentionally keep
21
+ * their literal colours and ignore this variable. SSR-safe (no-op without DOM).
22
+ */
23
+ export declare const applyIconColor: (config?: Pick<IConfig, "colors"> | null) => void;
@@ -0,0 +1,8 @@
1
+ import { TypographyConfig } from '../types/models/config.model';
2
+ /**
3
+ * Loads and applies the host-provided font configuration whenever it changes.
4
+ * Wired into the chat root (ReduxWrapper) so the font is in place before the
5
+ * UI paints. No-op when `typography` is undefined — the default system font
6
+ * stack is kept, so existing integrations are unaffected.
7
+ */
8
+ export declare function useTypography(typography?: TypographyConfig): void;
@@ -10,6 +10,15 @@ interface UnreadMessagesStats {
10
10
  unreadByRoom: UnreadMessagesMap;
11
11
  displayTotal: string;
12
12
  displayByRoom: UnreadMessagesDisplayMap;
13
+ /**
14
+ * True while the unread counts are not trustworthy yet — i.e. until the
15
+ * room list has been populated at least once (from cache or the API), or
16
+ * until the first rooms load cycle finishes for an account with no rooms.
17
+ * Lets consumers show a spinner/skeleton instead of a misleading "0".
18
+ */
19
+ loading: boolean;
20
+ /** Alias of `loading` — matches the React Native SDK field name. */
21
+ isLoading: boolean;
13
22
  }
14
23
  export declare const useUnreadMessagesCounter: () => UnreadMessagesStats;
15
24
  export { useUnreadMessagesCounter as useUnread };
package/dist/main.css CHANGED
@@ -1 +1 @@
1
- body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}html,body,#root{height:100%;margin:0;padding:0}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background-color:#5e3fde;border-radius:6px}::-webkit-scrollbar-track{background-color:#f0f0f0}*{scrollbar-width:thin;scrollbar-color:#5e3fde #f0f0f0}.apple-emoji{font-family:"Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif}.emoji-mart{max-width:320px;max-height:360px;overflow-y:auto;border-radius:12px;box-shadow:0 4px 12px #0000001a}.message-highlight{animation:messageHighlight 2s ease-out;transition:background-color .3s ease}@keyframes messageHighlight{0%{background-color:#2196f366;transform:scale(1.02);box-shadow:0 0 0 2px #2196f333}30%{background-color:#2196f34d;box-shadow:0 0 0 2px #2196f326}60%{background-color:#2196f333;box-shadow:0 0 0 1px #2196f31a}to{background-color:transparent;transform:scale(1);box-shadow:none}}@media(max-width:768px){.message-notification-container{left:50%!important;right:auto!important;transform:translate(-50%)!important;align-items:center!important}.message-notification-container[data-position-horizontal=left],.message-notification-container[data-position-horizontal=right]{left:50%!important;right:auto!important;transform:translate(-50%)!important}}
1
+ body{margin:0;font-family:var( --ethora-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif );-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}html,body,#root{height:100%;margin:0;padding:0}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background-color:#5e3fde;border-radius:6px}::-webkit-scrollbar-track{background-color:#f0f0f0}*{scrollbar-width:thin;scrollbar-color:#5e3fde #f0f0f0}.apple-emoji{font-family:"Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif}.emoji-mart{max-width:320px;max-height:360px;overflow-y:auto;border-radius:12px;box-shadow:0 4px 12px #0000001a}.message-highlight{animation:messageHighlight 2s ease-out;transition:background-color .3s ease}@keyframes messageHighlight{0%{background-color:#2196f366;transform:scale(1.02);box-shadow:0 0 0 2px #2196f333}30%{background-color:#2196f34d;box-shadow:0 0 0 2px #2196f326}60%{background-color:#2196f333;box-shadow:0 0 0 1px #2196f31a}to{background-color:transparent;transform:scale(1);box-shadow:none}}@media(max-width:768px){.message-notification-container{left:50%!important;right:auto!important;transform:translate(-50%)!important;align-items:center!important}.message-notification-container[data-position-horizontal=left],.message-notification-container[data-position-horizontal=right]{left:50%!important;right:auto!important;transform:translate(-50%)!important}}
package/dist/main.d.ts CHANGED
@@ -9,4 +9,7 @@ export { useQRCodeChat, handleQRChatId } from './hooks/useQRCodeChatHandler';
9
9
  export { useInAppNotifications } from './hooks/useInAppNotifications';
10
10
  export { default as usePushNotifications } from './hooks/usePushNotifications';
11
11
  export { resendMessage } from './utils/resendMessage';
12
+ export { applyTypography, clearTypography } from './helpers/applyTypography';
13
+ export { useTypography } from './hooks/useTypography';
14
+ export type { TypographyConfig, FontFaceSource, } from './types/models/config.model';
12
15
  export { ChatInputTestIds, MessageBubbleTestIds, RoomListTestIds, AuthTestIds, } from './testIds';