feat(chat): implement real-time chat system, AI chatbot, group management, and attachments#67
Merged
Merged
Conversation
Chat System: - TypeScript types + normalizers for conversations/messages - Zustand stores: chat-store, presence-store - Providers: ChatProvider (SSE events), PresenceProvider (heartbeat) - TanStack Query hooks: useConversations, useMessages, useSendMessage - BFF API routes: 9 routes for chat + presence endpoints - SSE stream extended with event: chat frames - UI: ConversationList, MessageThread, MessageBubble (read receipts), ChatFab, ChatDrawer (Sheet right), /chat email-client page - gRPC clients: getChatClient(), getPresenceClient() AI Chatbot: - DeepSeek streaming client with SYSTEM_PROMPT (Indonesian) - 7-layer guardrails: prompt injection detection (10 regex patterns) - 10 read-only tools with Zod validation + tool executor - Streaming BFF route with tool call handling - Zustand persist store (survives navigation, localStorage) - ChatbotPanel (fixed floating), ChatbotFab, streaming UI - Dashboard layout wired with both FABs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Browser Notification API: shows desktop notification when chat message arrives while tab is hidden or user is viewing a different conversation - Notification sound: 300ms sine wave beep via AudioContext - Tab title badge: shows unread count as (N) prefix - Permission request: prompted once on auth via notification-provider Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New Conversation dialog: swap raw UUID Input for searchable UserPicker combobox with debounced search by name/email - UserPicker: add showOnlineStatus prop — green/gray dot per user - ConversationList: add "Online Now" avatar row at top — click to DM - ChatFab: show online user count badge + tooltip Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nline users fetch 1. Chat Drawer: w-full responsive width, single-panel mobile layout with back button (hides conversation list when thread is active) 2. New Conversation: add error toast on mutation failure, close dialog on success even if conversationId is empty 3. Presence Provider: fetch all online users on mount + refresh every 60s so "Online Now" section works without chat history Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The selector `s.messages[conversationId] ?? []` creates a new array reference on every getSnapshot call, violating React's requirement that getSnapshot results be cached. Use useShallow + useMemo to stabilize the reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace manual Avatar/AvatarFallback with UserAvatar (resolves profile photo + initials via useUser hook) and UserName (resolves full name). Fixes "Unknown User" / "UN" initials for online users whose data wasn't in the lightweight lookup map. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…from flat fields SSE bridge: flattenChatEvent() converts proto StreamChatEventsResponse (nested oneof) into flat ChatSSEEvent fields the store expects. ChatProvider: builds ChatMessage object from flat SSE fields when message_received/edited arrives without a nested message object. Enables real-time chat delivery without page reload. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Browser notification: document.hidden → document.hasFocus() Shows notification when window unfocused (not just tab hidden) 2. Chat store handleSSEEvent: on message_received, also update conversation's lastMessage preview and increment unreadCount (skip increment if conversation is currently active) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…close button
Fix 1-2: Conversation preview — compact time format (5m/14:30/Yesterday/Jul 15),
proper truncation layout, lastMessage + unreadCount live update via SSE
Fix 3: Thread header with conversation name + close (X) button — works in
both drawer and /chat page, deselects active conversation
Fix 4: Typing indicator shows actual user names ("Ilham is typing...")
instead of "Someone is typing..." — store tracks {id, name} pairs
Wire 7: Edit message — inline textarea on hover menu, Enter to save
Wire 8: Delete message — ConfirmDialog, soft-delete shows [deleted]
Wire 9: Edit history dialog — view previous versions with timestamps
New files: edit-history-dialog.tsx
New hooks: useEditMessage, useDeleteMessage, useEditHistory
New types: RawEditHistoryEntry, normalizeEditHistoryEntry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…B theme
E+F: Drawer showCloseButton={false} — suppress default Sheet X that
overlapped with conversation list + button and thread close button.
Added dedicated close button in conversation list header instead.
G: Typing indicator fallback — uses <UserName> component when SSE
userName is empty, resolves actual name from API.
H: Online users — cap to 8 visible avatars + "+N more" badge,
hidden scrollbar, edge fade gradient indicator.
I: ChatFab variant toggle — matches ChatbotFab pattern:
variant={isOpen ? "default" : "secondary"}
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sender auto-creates a read receipt on send. In 2-person DM this made receipts.length=1 >= participantCount-1=1, showing blue double-check immediately. Now filters out sender's own userId before computing readByAll/delivered thresholds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend for the three remaining chat features, consistent across both the chat drawer and the /chat page. Group management: - New Group dialog, group settings panel (rename, add/remove members, leave), and hooks (useAddParticipants, useRemoveParticipant, useUpdateGroup, useLeaveConversation) with participant BFF routes. Clear history: - useClearHistory hook + DELETE history BFF route, wired into the thread menu. Attachments: - Upload BFF route (multipart, 25MB, type validation) + useUploadAttachment. - attachment-preview (inline images with lightbox, downloadable file chips), paperclip picker with pending chips and upload progress in MessageInput, attachment rendering in MessageBubble, and "attachment" last-message preview. - SSE bridge threads attachments through real-time message events. Also resolves react-compiler lint errors in the chat dialogs and provider by replacing setState-in-effect resets with onOpenChange handlers and the adjust-state-during-render pattern. Authored-By: Ilham R <ilhamram332@gmail.com> Co-Authored-By: Ilham R <me@ilramdhan.dev> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: IT Mutugading <it@mutugading.com> Co-Authored-By: Indra Putro <indraputro@mutugading.com> Co-Authored-By: Ilham R <ilham.ramadhan@mutugading.com> Co-Authored-By: IT Mutu Gading <mutugadingapps@gmail.com>
The chat store dropped read_receipt SSE events (default case), so a sender never learned the peer had read their messages until a full refetch — the checkmark stayed a single gray tick even after the peer read and replied. Add a markReadBy store action and a read_receipt case in handleSSEEvent that appends the reader's receipt to every message they can see, flipping the sender's checkmark to blue in real time. Authored-By: Ilham R <ilhamram332@gmail.com> Co-Authored-By: Ilham R <me@ilramdhan.dev> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: IT Mutugading <it@mutugading.com> Co-Authored-By: Indra Putro <indraputro@mutugading.com> Co-Authored-By: Ilham R <ilham.ramadhan@mutugading.com> Co-Authored-By: IT Mutu Gading <mutugadingapps@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Pull Request ini menghadirkan fitur Real-Time Chat System dan integrasi AI Chatbot secara komprehensif. Inisiatif ini mencakup pembuatan sistem pengiriman pesan antar-pengguna (peer-to-peer & grup) dengan dukungan Server-Sent Events (SSE) untuk pembaruan instan, Presence Provider untuk status online dan typing, serta dukungan file attachments hingga 25MB.
Sebagai tambahan, PR ini menyertakan AI Chatbot pintar (ditenagai oleh model DeepSeek) yang dilengkapi dengan 7-layer guardrails dan fungsi pemanggilan alat (tool executor) secara streaming. Berbagai UI/UX polish tingkat lanjut—seperti browser push notifications beserta efek suara, laci obrolan responsif (chat drawer), status read receipts (centang biru), hingga manajemen grup—turut diimplementasikan untuk memberikan pengalaman komunikasi yang mulus di dalam platform.
Type of Change
Module/Component Affected
Changes Made
1. Real-Time Chat & Presence System:
PresenceProvideryang melakukan mekanisme heartbeat untuk melacak dan menampilkan pengguna yang sedang online beserta indikator status pengetikan (typing indicator) menggunakan nama asli viaUserName.AudioContextserta badge notifikasi unread di judul tab.2. AI Chatbot Integration:
3. Advanced Chat Features (Group, Attachments, Modifiers):
MessageInput.[deleted]), fitur clear history, serta panel riwayat perubahan pesan (edit history).read_receiptSSE), dengan mengecualikan laporan baca milik pengirim sendiri.4. UI Polish & Critical Fixes:
MessageThreadakibat referensi array baru di dalam selector dengan mengimplementasikan polauseShallowdanuseMemo.ChatDrawer) responsif dengan tata letak panel tunggal (single-panel layout) di perangkat seluler yang menyembunyikan daftar conversation saat thread sedang aktif.UserPickercombobox yang dilengkapi fungsi pencarian otomatis dan titik indikator status online.onOpenChangeagar sesuai dengan best practice dan menghilangkan peringatan lint.Related Issues
Fixes #
Related to #
Screenshots
Before
After
Testing Performed
Manual Testing
Browser Testing
Build Verification
npm run lintpassesnpx tsc --noEmitpassesnpm run buildsucceedsAccessibility
Performance
Pre-merge Checklist
Reviewer Notes
useShallowdari Zustand. Jangan me-return nilai fallback array baru[]langsung di dalam fungsi selector karena akan memicu render tiada akhir (infinite loop) akibat kegagalan caching getSnapshot pada kapabilitas React rendering.flattenChatEvent()untuk menyesuaikan dengan kebutuhan manajemen state frontend sehingga pembaruan UI instan tanpa reload halaman dapat tereksekusi dengan efisien.