@college-africa/chat-ui 0.1.0

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.
Files changed (35) hide show
  1. package/README.md +141 -0
  2. package/dist/__tests__/setup.d.ts +0 -0
  3. package/dist/cache/keys.d.ts +8 -0
  4. package/dist/components/ChatApp.d.ts +24 -0
  5. package/dist/components/ChatContainer.d.ts +12 -0
  6. package/dist/components/ChatHeader.d.ts +18 -0
  7. package/dist/components/ChatWindow.d.ts +17 -0
  8. package/dist/components/ConversationItem.d.ts +17 -0
  9. package/dist/components/ConversationItemSkeleton.d.ts +6 -0
  10. package/dist/components/ConversationList.d.ts +14 -0
  11. package/dist/components/ErrorBoundary.d.ts +25 -0
  12. package/dist/components/LoadMoreTrigger.d.ts +18 -0
  13. package/dist/components/MessageBubble.d.ts +23 -0
  14. package/dist/components/MessageInput.d.ts +16 -0
  15. package/dist/components/MessageList.d.ts +27 -0
  16. package/dist/components/MessageSkeleton.d.ts +10 -0
  17. package/dist/components/UnreadBadge.d.ts +14 -0
  18. package/dist/components/index.d.ts +13 -0
  19. package/dist/hooks/constants.d.ts +12 -0
  20. package/dist/hooks/useEffectAsync.d.ts +8 -0
  21. package/dist/hooks/useMessages.d.ts +21 -0
  22. package/dist/hooks/useSenderNames.d.ts +3 -0
  23. package/dist/hooks/useWebSocketMessages.d.ts +23 -0
  24. package/dist/index.d.ts +41 -0
  25. package/dist/index.js +76 -0
  26. package/dist/index.mjs +8056 -0
  27. package/dist/types/cache.d.ts +37 -0
  28. package/dist/types/index.d.ts +22 -0
  29. package/dist/utils/date.d.ts +32 -0
  30. package/dist/utils/db.d.ts +21 -0
  31. package/dist/utils/messages.d.ts +30 -0
  32. package/dist/utils/sdk.d.ts +24 -0
  33. package/dist/utils/summaries.d.ts +9 -0
  34. package/dist/utils/theme.d.ts +1 -0
  35. package/package.json +66 -0
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # Chat UI Component Library
2
+
3
+ React chat UI component library for College Africa Chat service. Built with Material UI and TypeScript.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Material UI Integration** - Beautiful, accessible components
8
+ - 🚀 **Optimistic Updates** - Instant feedback for better UX
9
+ - 💾 **Offline-First** - IndexedDB caching with background sync
10
+ - 🔌 **WebSocket Support** - Real-time message delivery
11
+ - 📱 **Responsive Design** - Works on all screen sizes
12
+ - 🎯 **TypeScript** - Full type safety
13
+ - âš¡ **Vite Build** - Fast development and optimized production builds
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ # This package is part of the monorepo
19
+ yarn workspace ui install
20
+ ```
21
+
22
+ ## Development
23
+
24
+ ```bash
25
+ # Run demo application
26
+ yarn workspace ui dev
27
+
28
+ # Build library
29
+ yarn workspace ui build
30
+
31
+ # Run tests
32
+ yarn workspace ui test
33
+
34
+ # Run tests with UI
35
+ yarn workspace ui test:ui
36
+
37
+ # Type check
38
+ yarn workspace ui typecheck
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ```tsx
44
+ import { ChatApp, ChatContainer, theme } from 'ui';
45
+ import { ThemeProvider } from '@mui/material/styles';
46
+ import CssBaseline from '@mui/material/CssBaseline';
47
+
48
+ function App() {
49
+ return (
50
+ <ThemeProvider theme={theme}>
51
+ <CssBaseline />
52
+ <ChatApp
53
+ config={{ baseURL: 'https://chat.college.africa' }}
54
+ token={yourJwtToken}
55
+ >
56
+ <ChatContainer />
57
+ </ChatApp>
58
+ </ThemeProvider>
59
+ );
60
+ }
61
+ ```
62
+
63
+ ## Architecture
64
+
65
+ ### Component Structure
66
+ ```
67
+ ChatApp (SDK initialization wrapper)
68
+ └── ChatContainer (full-page layout)
69
+ ├── ConversationList (sidebar)
70
+ │ └── ConversationItem[] (with unread badges)
71
+ └── ChatWindow (selected conversation)
72
+ ├── MessageList (infinite scroll)
73
+ └── MessageInput
74
+ ```
75
+
76
+ ### State Management
77
+ - **Singleton SDK Pattern** - No React Context needed
78
+ - **Local Component State** - Each component manages its own data
79
+ - **IndexedDB Cache** - Offline-first with background sync
80
+
81
+ ### Cache Strategy
82
+ 1. Load cached data instantly (if available)
83
+ 2. Fetch fresh data in background
84
+ 3. Update UI with fresh data
85
+ 4. WebSocket updates append to cache
86
+
87
+ ## API
88
+
89
+ ### Components
90
+ - `ChatApp` - SDK initialization wrapper
91
+ - `ChatContainer` - Main chat container
92
+ - `ConversationList` - List of conversations
93
+ - `ChatWindow` - Message display and input
94
+
95
+ ### Hooks
96
+ - `useMessages` - Fetch and manage messages
97
+ - `useSendMessage` - Send messages with optimistic updates
98
+ - `useConversationList` - Fetch conversation list with unread counts
99
+
100
+ ### SDK Singleton
101
+ ```tsx
102
+ import { initSDK, getSDK, disconnectSDK } from 'ui';
103
+
104
+ // Initialize (done by ChatApp component)
105
+ await initSDK({ baseURL: 'https://...' }, token);
106
+
107
+ // Access anywhere in your app
108
+ const sdk = getSDK();
109
+ await sdk.sendGroupMessage({ groupId: '123', content: 'Hello' });
110
+
111
+ // Cleanup on logout
112
+ disconnectSDK();
113
+ ```
114
+
115
+ ### Theme
116
+ ```tsx
117
+ import { theme } from 'ui';
118
+
119
+ // Use the theme
120
+ <ThemeProvider theme={theme}>
121
+ {/* ... */}
122
+ </ThemeProvider>
123
+
124
+ // Access chat colors for custom components
125
+ <Box sx={{ backgroundColor: "primary.main" }}>
126
+ {/* ... */}
127
+ </Box>
128
+ ```
129
+
130
+ ## Development Status
131
+
132
+ - [x] Phase 1: Foundation (setup, utils, theme, cache)
133
+ - [ ] Phase 2: SDK & Hooks
134
+ - [ ] Phase 3: Core Components
135
+ - [ ] Phase 4: Real-Time Features
136
+ - [ ] Phase 5: Demo Application
137
+ - [ ] Phase 6: Polish & Documentation
138
+
139
+ ## License
140
+
141
+ Private - College Africa
File without changes
@@ -0,0 +1,8 @@
1
+ import { Conversation, Message } from '../types';
2
+ /** Get conversation id from conversation */
3
+ export declare const getConverSationId: ({ type, id }: Conversation) => string;
4
+ export declare const getSummaryId: ({ group, user }: {
5
+ group?: string | number;
6
+ user?: string | number;
7
+ }) => `group:${string}` | `group:${number}` | `user:${string}` | `user:${number}`;
8
+ export declare const getMessageConversationId: (message: Message, currentUserId: number) => string;
@@ -0,0 +1,24 @@
1
+ import { default as React } from 'react';
2
+ import { ChatClientConfig } from 'sdk';
3
+ export interface ChatAppProps {
4
+ /** SDK configuration */
5
+ config: ChatClientConfig;
6
+ /** Authentication token (optional, can be set later via SDK) */
7
+ token?: string;
8
+ /** Token fetcher function (alternative to token prop) */
9
+ getToken?: () => Promise<string>;
10
+ /** Child components to render when SDK is ready */
11
+ children: React.ReactNode;
12
+ /** Custom loading component */
13
+ LoadingComponent?: React.ComponentType;
14
+ /** Custom error component */
15
+ ErrorComponent?: React.ComponentType<{
16
+ error: Error;
17
+ retry: () => void;
18
+ }>;
19
+ }
20
+ /**
21
+ * ChatApp component
22
+ * Initializes the SDK singleton and manages its lifecycle
23
+ */
24
+ export declare const ChatApp: React.FC<ChatAppProps>;
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ import { Conversation } from '../types';
3
+ export interface ChatContainerProps {
4
+ /** Initial conversation to display (optional) */
5
+ initialConversation?: Conversation;
6
+ }
7
+ /**
8
+ * ChatContainer component
9
+ * Two-column layout with conversation list sidebar and chat window
10
+ * On mobile (<=sm), shows conversation list or chat window in fullwidth, with back navigation
11
+ */
12
+ export declare const ChatContainer: React.FC<ChatContainerProps>;
@@ -0,0 +1,18 @@
1
+ import { default as React } from 'react';
2
+ export interface ChatHeaderProps {
3
+ /** Display name of the conversation */
4
+ displayName: string;
5
+ /** Type of conversation */
6
+ type: "group" | "dm";
7
+ /** Optional subtitle (e.g., "3 members", "Active now") */
8
+ subtitle?: string;
9
+ /** WebSocket connection status */
10
+ isConnected?: boolean;
11
+ /** Back handler for mobile navigation (optional) */
12
+ onBack?: () => void;
13
+ }
14
+ /**
15
+ * ChatHeader component
16
+ * Shows conversation name, avatar, optional subtitle, and connection status
17
+ */
18
+ export declare const ChatHeader: React.FC<ChatHeaderProps>;
@@ -0,0 +1,17 @@
1
+ import { default as React } from 'react';
2
+ import { User, Conversation } from '../types';
3
+ export interface ChatWindowProps {
4
+ /** Conversation object containing type, ID, and optional name */
5
+ conversation: Conversation;
6
+ /** Current user (fetched by ChatContainer) */
7
+ user: User;
8
+ /** WebSocket connection status */
9
+ /** Back handler for mobile navigation (optional) */
10
+ onBack?: () => void;
11
+ isConnected: boolean;
12
+ }
13
+ /**
14
+ * ChatWindow component
15
+ * Complete chat interface with header, message list, and input
16
+ */
17
+ export declare const ChatWindow: React.FC<ChatWindowProps>;
@@ -0,0 +1,17 @@
1
+ import { default as React } from 'react';
2
+ import { ConversationSummary } from 'sdk';
3
+ export interface ConversationItemProps {
4
+ /** Conversation summary data */
5
+ conversation: ConversationSummary;
6
+ /** Display name for the conversation */
7
+ displayName: string;
8
+ /** Whether this conversation is selected */
9
+ selected: boolean;
10
+ /** Click handler */
11
+ onClick: () => void;
12
+ }
13
+ /**
14
+ * ConversationItem component
15
+ * Shows conversation name, last message preview, timestamp, and unread badge
16
+ */
17
+ export declare const ConversationItem: React.FC<ConversationItemProps>;
@@ -0,0 +1,6 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * ConversationItemSkeleton component
4
+ * Displays a loading skeleton for conversation list items
5
+ */
6
+ export declare const ConversationItemSkeleton: React.FC;
@@ -0,0 +1,14 @@
1
+ import { default as React } from 'react';
2
+ import { Conversation, User } from '../types';
3
+ export interface ConversationListProps {
4
+ currentUser?: User;
5
+ /** Currently selected conversation */
6
+ selectedConversation: Conversation | null;
7
+ /** Handler for conversation selection */
8
+ onConversationSelect: (conversation: Conversation) => void;
9
+ }
10
+ /**
11
+ * ConversationList component
12
+ * Displays a scrollable list of all conversations with summaries
13
+ */
14
+ export declare const ConversationList: React.FC<ConversationListProps>;
@@ -0,0 +1,25 @@
1
+ import { Component, ErrorInfo, ReactNode } from 'react';
2
+ interface ErrorBoundaryProps {
3
+ /** Child components to wrap */
4
+ children: ReactNode;
5
+ /** Optional fallback UI */
6
+ fallback?: ReactNode;
7
+ /** Optional error handler callback */
8
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
9
+ }
10
+ interface ErrorBoundaryState {
11
+ hasError: boolean;
12
+ error: Error | null;
13
+ }
14
+ /**
15
+ * ErrorBoundary component
16
+ * Catches React errors in child components and displays a fallback UI
17
+ */
18
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
19
+ constructor(props: ErrorBoundaryProps);
20
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
21
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
22
+ handleReset: () => void;
23
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
24
+ }
25
+ export {};
@@ -0,0 +1,18 @@
1
+ import { default as React } from 'react';
2
+ export interface LoadMoreTriggerProps {
3
+ /** Whether there are more items to load */
4
+ hasMore: boolean;
5
+ /** Whether currently loading */
6
+ loading: boolean;
7
+ /** Callback to load more items */
8
+ onLoadMore: () => void;
9
+ /** Optional threshold for triggering (0.0 to 1.0, default: 1.0) */
10
+ threshold?: number;
11
+ /** Optional root margin (default: '100px') */
12
+ rootMargin?: string;
13
+ }
14
+ /**
15
+ * LoadMoreTrigger component
16
+ * Automatically triggers onLoadMore when the component is scrolled into view
17
+ */
18
+ export declare const LoadMoreTrigger: React.FC<LoadMoreTriggerProps>;
@@ -0,0 +1,23 @@
1
+ import { default as React } from 'react';
2
+ import { Message } from 'sdk';
3
+ export interface MessageBubbleProps {
4
+ /** The message to display */
5
+ message: Message;
6
+ /** Whether this message was sent by the current user */
7
+ isOwnMessage: boolean;
8
+ /** Optional sender name to display (if not provided, shows "User {id}") */
9
+ senderName?: string;
10
+ /** Whether to show sender name (hide for grouped messages) */
11
+ showSenderName?: boolean;
12
+ /** Whether this is the first message in a group */
13
+ isFirstInGroup?: boolean;
14
+ /** Whether this is the last message in a group */
15
+ isLastInGroup?: boolean;
16
+ /** Message send status (for optimistic UI) */
17
+ status?: "sending" | "sent" | "failed";
18
+ }
19
+ /**
20
+ * MessageBubble component
21
+ * Displays a single message with sender info, content, and timestamp
22
+ */
23
+ export declare const MessageBubble: React.FC<MessageBubbleProps>;
@@ -0,0 +1,16 @@
1
+ import { default as React } from 'react';
2
+ export interface MessageInputProps {
3
+ /** Callback when a message is sent */
4
+ onSend: (content: string) => void;
5
+ /** Whether a message is currently being sent */
6
+ sending?: boolean;
7
+ /** Placeholder text */
8
+ placeholder?: string;
9
+ /** Disabled state */
10
+ disabled?: boolean;
11
+ }
12
+ /**
13
+ * MessageInput component
14
+ * Text field with send button for composing and sending messages
15
+ */
16
+ export declare const MessageInput: React.FC<MessageInputProps>;
@@ -0,0 +1,27 @@
1
+ import { default as React } from 'react';
2
+ import { Message } from 'sdk';
3
+ export interface MessageListProps {
4
+ /** List of messages to display */
5
+ messages: Message[];
6
+ /** ID of the current user */
7
+ currentUserId: number;
8
+ /** Whether messages are loading */
9
+ loading?: boolean;
10
+ /** Error message if any */
11
+ error?: Error | null;
12
+ /** Whether to auto-scroll to bottom on new messages */
13
+ autoScroll?: boolean;
14
+ /** Whether there are more messages to load */
15
+ hasMore?: boolean;
16
+ /** Whether currently loading more messages */
17
+ loadingMore?: boolean;
18
+ /** Callback to load more messages */
19
+ onLoadMore?: () => void;
20
+ /** Force scroll to bottom (e.g., when user sends a message) */
21
+ forceScrollToBottom?: boolean;
22
+ }
23
+ /**
24
+ * MessageList component
25
+ * Displays a scrollable list of messages with auto-scroll to bottom
26
+ */
27
+ export declare const MessageList: React.FC<MessageListProps>;
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ export interface MessageSkeletonProps {
3
+ /** Whether this is the current user's message (right-aligned) */
4
+ isOwnMessage?: boolean;
5
+ }
6
+ /**
7
+ * MessageSkeleton component
8
+ * Displays a loading skeleton for message bubbles
9
+ */
10
+ export declare const MessageSkeleton: React.FC<MessageSkeletonProps>;
@@ -0,0 +1,14 @@
1
+ import { default as React } from 'react';
2
+ export interface UnreadBadgeProps {
3
+ /** Number of unread messages */
4
+ count: number;
5
+ /** Child element to wrap */
6
+ children: React.ReactNode;
7
+ /** Maximum number to display (shows "99+" if exceeded) */
8
+ max?: number;
9
+ }
10
+ /**
11
+ * UnreadBadge component
12
+ * Displays a badge with unread message count
13
+ */
14
+ export declare const UnreadBadge: React.FC<UnreadBadgeProps>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Components barrel export
3
+ */
4
+ export { ChatApp } from './ChatApp';
5
+ export type { ChatAppProps } from './ChatApp';
6
+ export { ChatWindow } from './ChatWindow';
7
+ export type { ChatWindowProps } from './ChatWindow';
8
+ export { MessageList } from './MessageList';
9
+ export type { MessageListProps } from './MessageList';
10
+ export { MessageBubble } from './MessageBubble';
11
+ export type { MessageBubbleProps } from './MessageBubble';
12
+ export { MessageInput } from './MessageInput';
13
+ export type { MessageInputProps } from './MessageInput';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Cache TTL constants (in milliseconds)
3
+ */
4
+ export declare const CACHE_TTL: {
5
+ readonly SUMMARIES: number;
6
+ readonly USER: number;
7
+ readonly MESSAGES: number;
8
+ };
9
+ /**
10
+ * Default stale time for cached data (5 minutes)
11
+ */
12
+ export declare const DEFAULT_STALE_TIME: number;
@@ -0,0 +1,8 @@
1
+ import { DependencyList } from 'react';
2
+ /**
3
+ * Asynchronous version of React.useEffect
4
+ * @param {Function} fn Async function to run
5
+ * @param {Array} deps Hook dependencies
6
+ */
7
+ declare const useEffectAsync: (fn: () => Promise<void>, deps: DependencyList, teardown?: () => void) => void;
8
+ export default useEffectAsync;
@@ -0,0 +1,21 @@
1
+ import { Conversation } from '../types';
2
+ export interface UseMessagesParams {
3
+ /** Conversation object containing type and ID */
4
+ conversation: Conversation;
5
+ /** Number of messages to fetch per page */
6
+ limit?: number;
7
+ }
8
+ /**
9
+ * useMessages - Hook for managing messages in a conversation
10
+ *
11
+ * Uses IndexedDB as the single source of truth.
12
+ * All business logic (deduplication, merging, transformations) lives here.
13
+ */
14
+ export declare function useMessages({ conversation, limit }: UseMessagesParams): {
15
+ messages: import('../types').CachedMessage[];
16
+ cursor: import('../types').CachedCursor | undefined;
17
+ loading: boolean;
18
+ error: Error | undefined;
19
+ loadMore: () => Promise<void>;
20
+ loadingMore: boolean;
21
+ };
@@ -0,0 +1,3 @@
1
+ import { Message } from 'sdk';
2
+ /** Get a map of <userid, username> for message senders */
3
+ export declare function useSenderNames(messages: Message[]): Map<number, string>;
@@ -0,0 +1,23 @@
1
+ import { Message } from 'sdk';
2
+ import { Conversation } from '../types';
3
+ export interface UseWebSocketMessagesParams {
4
+ /** Callback when a new message is received */
5
+ onMessage?: (message: Message) => void;
6
+ /** Callback when WebSocket connection opens */
7
+ onConnect?: () => void;
8
+ /** Callback when WebSocket connection closes */
9
+ onDisconnect?: (code: number, reason: string) => void;
10
+ /** Callback when WebSocket error occurs */
11
+ onError?: (error: Error) => void;
12
+ /** Current user ID for filtering messages */
13
+ currentUserId?: number;
14
+ /** Current conversation object */
15
+ conversation?: Conversation | null;
16
+ /** Whether to automatically update cache */
17
+ updateCache?: boolean;
18
+ }
19
+ /**
20
+ * Hook for handling WebSocket message events
21
+ * Automatically subscribes/unsubscribes to WebSocket events
22
+ */
23
+ export declare function useWebSocketMessages({ onMessage, onConnect, onDisconnect, onError, currentUserId, conversation, updateCache }?: UseWebSocketMessagesParams): void;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Chat UI Component Library
3
+ * React components and hooks for building chat interfaces
4
+ */
5
+ export { ChatApp } from './components/ChatApp';
6
+ export type { ChatAppProps } from './components/ChatApp';
7
+ export { ChatContainer } from './components/ChatContainer';
8
+ export type { ChatContainerProps } from './components/ChatContainer';
9
+ export { ChatWindow } from './components/ChatWindow';
10
+ export type { ChatWindowProps } from './components/ChatWindow';
11
+ export { ChatHeader } from './components/ChatHeader';
12
+ export type { ChatHeaderProps } from './components/ChatHeader';
13
+ export { ConversationList } from './components/ConversationList';
14
+ export type { ConversationListProps } from './components/ConversationList';
15
+ export { ConversationItem } from './components/ConversationItem';
16
+ export type { ConversationItemProps } from './components/ConversationItem';
17
+ export { MessageList } from './components/MessageList';
18
+ export type { MessageListProps } from './components/MessageList';
19
+ export { MessageBubble } from './components/MessageBubble';
20
+ export type { MessageBubbleProps } from './components/MessageBubble';
21
+ export { MessageInput } from './components/MessageInput';
22
+ export type { MessageInputProps } from './components/MessageInput';
23
+ export { LoadMoreTrigger } from './components/LoadMoreTrigger';
24
+ export type { LoadMoreTriggerProps } from './components/LoadMoreTrigger';
25
+ export { UnreadBadge } from './components/UnreadBadge';
26
+ export type { UnreadBadgeProps } from './components/UnreadBadge';
27
+ export { ErrorBoundary } from './components/ErrorBoundary';
28
+ export { ConversationItemSkeleton } from './components/ConversationItemSkeleton';
29
+ export { MessageSkeleton } from './components/MessageSkeleton';
30
+ export type { MessageSkeletonProps } from './components/MessageSkeleton';
31
+ export { initSDK, getSDK, disconnectSDK, isSDKInitialized } from './utils/sdk';
32
+ export { groupMessages, shouldGroupMessages } from './utils/messages';
33
+ export type { MessageGroup } from './utils/messages';
34
+ export { useMessages } from './hooks/useMessages';
35
+ export type { UseMessagesParams } from './hooks/useMessages';
36
+ export { useWebSocketMessages } from './hooks/useWebSocketMessages';
37
+ export type { UseWebSocketMessagesParams } from './hooks/useWebSocketMessages';
38
+ export { useSenderNames } from './hooks/useSenderNames';
39
+ export { CACHE_TTL } from './hooks/constants';
40
+ export type { User, Message, Group, ConversationSummary, ChatRole, Cursor, MessagePage, ConnectionState, Conversation } from './types';
41
+ export { theme } from './utils/theme';