Appearance
Conversation - Technical Map
Auto-generated từ code thực tế ngày 2026-03-23.
Routes và module shell
| Item | Giá trị | Bằng chứng |
|---|---|---|
| Module slug | conversation | types.ts:1-5 |
| Root route | /conversation | module.ts:11-26, types.ts:1-5 |
| Detail route | /conversation/:id | module.ts:17-24, types.ts:1-5 |
| Conference route name | /conversation/:id/conference | types.ts:5 |
| Stringee route name | /conversation/stringee | types.ts:2 |
| Navigation | Commented out, chưa bật sidebar item | module.ts:33-40 |
Route graph
text
/conversation
└─ /conversation/:id
└─ RouterViewFE pages và shell components
| Loại | File | Vai trò |
|---|---|---|
| Page | pages/ConversationDetail.tsx | Wrapper workspace, nhận id, phát action video sang conference route |
| Layout | Layout.ts | Dùng MainLayout làm shell |
| Component | components/Conversation/index.tsx | Header workspace, giữ props của message layer, hiện chưa mount body thật |
| Component | components/ConversationList.tsx | Infinite list, select conversation, update read_at |
| Component | components/ConversationMessages.tsx | Render message stream + composer |
| Component | components/ConversationProvider.tsx | Provider shell pass-through |
| Component | components/StringeeProviderWrapper.tsx | Fetch token và mount StringeeProvider |
| Component | components/StringeeConferenceDialog.tsx | Modal hội thoại video Stringee |
Compositions và store
| File | Vai trò | Ghi chú |
|---|---|---|
compositions/useConversation.ts | Composition placeholder | Trả object rỗng |
compositions/graphql.ts | GetStringeeToken query | Query stringeeToken(data) |
vendors/twilio/compositions/useTwilio.ts | Lấy token Twilio | Wrapper twilioToken(data) |
vendors/twilio/compositions/graphql.ts | GetTwilioToken query | Query twilioToken(data) |
stores/useConversationStore.ts | Conversation runtime state | conversations, messages, currentConversation, conversationCursor, inCall |
utils.ts | Merge messages đã sort | mergeSortedConversationMessages() |
Store behavior
| Action | Logic | Bằng chứng |
|---|---|---|
updateConversations(items) | Merge theo id, sort giảm dần theo last_message_created_at | useConversationStore.ts:23-45 |
updateConversationCursor(items) | Lấy timestamp nhỏ nhất trong batch để giữ cursor cũ nhất | useConversationStore.ts:46-73 |
updateConversationMessages(messages) | Group theo conversation_id, merge từng group bằng helper sort | useConversationStore.ts:74-85 |
addConversationCursor(data) | Update list và cursor trong một bước | useConversationStore.ts:87-90 |
GraphQL operations
FE GraphQL
| Operation | File | Input | Output | Dùng ở đâu |
|---|---|---|---|---|
GetStringeeToken | compositions/graphql.ts | StringeeTokenInput { room?, record? } | stringeeToken { token, room_token } | StringeeProviderWrapper, StringeeConferenceDialog |
GetTwilioToken | vendors/twilio/compositions/graphql.ts | TwilioTokenInput { room? } | twilioToken { token } | useTwilio, TwilioVideo overlay |
Hasura DB functions
| Function | Root field | Session argument | Vai trò |
|---|---|---|---|
search_conversation_current | custom root field theo metadata | hasura_session | Danh sách conversation runtime |
search_unread_conversation_messages | custom root field theo metadata | hasura_session | Tìm unread message feed |
graphql/conversation.graphqltrong module hiện không chứa document nào; các operation thật được colocate ở composition files bên cạnh component/runtime.
Backend service conversation-api
| Endpoint | Handler | Mục đích |
|---|---|---|
POST /actions | Hasura action router | twilioToken, createConference, stringeeToken, stringeeCallEvent |
POST /events | Event router | Hiện có scaffold event example |
GET /healthz | version endpoint | Health/version check |
Action map
| Action | Input | Output | Bằng chứng |
|---|---|---|---|
twilioToken | data.room | { token } | action/twilio.go:15-47 |
stringeeToken | data.room, data.record | { token, room_token } | action/stringee.go:17-64 |
stringeeCallEvent | payload call event từ Stringee | SCCO connect action | action/stringee.go:67-106 |
createConference | conversation_id, name, start_time, end_time, type, participants | conference_id, conference_url | action/conference.go:29-85 |
Action context
| Thành phần | Vai trò |
|---|---|
Access | Parse session variables của Hasura |
Controller | GQL access client để mutate conversation table |
Stringee | Client tạo token/room Stringee |
Twilio | Client tạo Twilio JWT |
GoogleAPIDefaultEmail | Workspace email mặc định cho Google Meet flow |
Vendor runtime
Stringee
| File | Vai trò | Ghi chú |
|---|---|---|
vendors/stringee/components/StringeeProvider.ts | Provide/connect/disconnect client | Emit connected, disconnected, chatMessage, chatMessageState |
vendors/stringee/components/StringeeConference/index.tsx | Conference shell | Preview, sidebar chat/participants, join/leave wiring |
vendors/stringee/components/StringeeConference/StringeeVideoConference.tsx | Room runtime | Join room, publish local track, subscribe remote tracks, share screen |
vendors/stringee/components/StringeeConference/StringeeConferenceActions.tsx | Control bar | Toggle video/audio/screen/device selection |
vendors/stringee/components/types.ts | Token / conference constants | STRINGEE_ERROR_ROOM_NOT_FOUND, StringeeVideoOptions |
Twilio
| File | Vai trò | Ghi chú |
|---|---|---|
vendors/twilio/components/TwilioVideo/index.tsx | Full-screen overlay entrypoint | Đọc conversationStore.currentConversation.video_room_id |
vendors/twilio/components/TwilioVideo/TwilioVideoPreview.tsx | Preview trước khi join | Cho phép bật/tắt video/mic |
vendors/twilio/components/TwilioVideo/TwilioVideo.tsx | Twilio room runtime | Connect room, attach tracks, disconnect cleanup |
DB model và permissions
Tables
| Table | Vai trò | Ghi chú |
|---|---|---|
conversation | Header hội thoại | Có video_room_provider, video_room_id, visible, closed |
conversation_current | View runtime | Denormalized feed cho list hiện tại |
conversation_member | Membership + read/mute/in_call | Lưu read_at, mute_until, is_admin |
conversation_message | Message body | Có content, content_data, subject_id, type, pinned |
conversation_message_reaction | Reaction | Gắn reaction theo message_id |
conversation_message_mention | Mention/read state | Có read_at cho mention |
app_conversation_setting | App-level setting | Có relationship sang conversation |
Permission matrix
| Area | Rule |
|---|---|
conversation select | Member của conversation mới đọc được |
conversation insert | User có thể tạo conversation mới, Hasura set created_by/updated_by |
conversation update/delete | Chỉ member hoặc member admin tùy operation |
conversation_member update | User chỉ cập nhật record của chính mình |
conversation_message insert | Chỉ khi conversation chưa closed và user là member |
conversation_message update/delete | Chỉ tác giả message |
| Reaction/Mention | Chỉ trong scope message/member phù hợp |
Ghi chú triển khai
| # | Note | Bằng chứng |
|---|---|---|
| 1 | ConversationProvider hiện là pass-through provider, chưa có logic data-fetch riêng. | ConversationProvider.tsx:1-20 |
| 2 | Conversation/index.tsx chưa render message list hay compose actions, nên workspace body vẫn là shell. | Conversation/index.tsx:10-29 |
| 3 | StringeeConferenceDialog phụ thuộc useUser() để map userId -> display_name/avatar cho participant list. | StringeeConferenceDialog.tsx:34-113 |
| 4 | StringeeConference giữ state participant/track ở local refs, không đẩy hết vào store global. | StringeeConference/index.tsx:91-320 |
| 5 | conversation-api chỉ expose POST /actions, POST /events, GET /healthz; không có REST CRUD riêng. | server/main.go:32-49 |
Rủi ro / Findings kỹ thuật
| # | Gap | Bằng chứng |
|---|---|---|
| 1 | ConversationList dùng helper mutation stub nên đường update read_at chưa hoàn chỉnh. | ConversationList.tsx:188-190 |
| 2 | useConversation.ts trống, không có orchestration layer riêng cho module. | useConversation.ts:1-6 |
| 3 | Twilio preview emit payload lệch tên field với parent, dễ làm state microphoneEnabled không phản ánh đúng. | TwilioVideoPreview.tsx:69-73, TwilioVideo/index.tsx:27-34 |
| 4 | Route conference không thấy route record độc lập trong tree router của repo. | types.ts:5, module.ts:27-31, src/router/routes.ts |
| 5 | conversation.graphql không chứa document, nên người đọc dễ nhầm là module có 1 file GraphQL duy nhất. | conversation.graphql trống, compositions/graphql.ts:29-36 |