336 lines
12 KiB
Markdown
336 lines
12 KiB
Markdown
# Voice Room Emoji Package Plan
|
|
|
|
## Goal
|
|
|
|
This document only records a feasible implementation plan. No business code is changed in this round.
|
|
|
|
Requirement summary:
|
|
|
|
- Add an emoji package button next to the voice-room chat entry.
|
|
- The emoji package button and text chat must open the same bottom composer sheet.
|
|
- Emoji packages are shown only on mic seats.
|
|
- Emoji packages must not appear in room chat messages, including the `All`, `Chat`, and `Gift` tabs.
|
|
- Wait for final backend data and UI assets before starting the real implementation.
|
|
|
|
## Current Codebase Status
|
|
|
|
The current project already has several reusable pieces, so the feature is feasible and does not need a brand-new architecture.
|
|
|
|
Reusable entry points:
|
|
|
|
- Bottom area layout: `lib/ui_kit/widgets/room/room_bottom_widget.dart`
|
|
- Chat composer popup: `lib/ui_kit/widgets/room/room_msg_input.dart`
|
|
- Seat emoji display: `lib/modules/room/seat/sc_seat_item.dart`
|
|
- Room RTM send/receive: `lib/services/audio/rtm_manager.dart`
|
|
- Seat transient state: `lib/services/audio/rtc_manager.dart`
|
|
- Seat model: `lib/shared/business_logic/models/res/mic_res.dart`
|
|
- Emoji material API: `lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart`
|
|
- Emoji material model: `lib/shared/business_logic/models/res/sc_room_emoji_res.dart`
|
|
- Existing room message type: `lib/app/constants/sc_room_msg_type.dart`
|
|
|
|
Confirmed reusable behavior in current code:
|
|
|
|
- `SCRoomMsgType.emoticons` already exists.
|
|
- The room receiver already handles `EMOTICONS` separately and calls `RtcProvider.starPlayEmoji(msg)`.
|
|
- `SCSeatItem` already has an `Emoticons` widget with queueing, fade animation, and auto-dismiss.
|
|
- The project already has `/material/emoji/all`, and the response model already supports category + emoji list.
|
|
|
|
## Recommended Architecture
|
|
|
|
Recommended direction: keep one composer sheet and support two entry modes.
|
|
|
|
Recommended entry modes:
|
|
|
|
- `text` mode: opened from the existing chat entry, keyboard focused by default.
|
|
- `emojiPackage` mode: opened from the new emoji package button, keyboard hidden by default, emoji package panel open by default.
|
|
|
|
Recommended widget strategy:
|
|
|
|
- Reuse `RoomMsgInput` instead of creating a second bottom sheet.
|
|
- Add an input parameter such as `initialMode` or `initialPanel`.
|
|
- Keep the text input area as the same top row.
|
|
- Use the lower content area to switch between text helper emoji and room emoji package content.
|
|
|
|
Why this is the best fit:
|
|
|
|
- It matches the requirement that text chat and emoji package use the same bottom popup.
|
|
- It keeps interaction and style in one place.
|
|
- It avoids future drift between two different room composer implementations.
|
|
|
|
## Recommended Interaction Flow
|
|
|
|
### 1. Bottom bar
|
|
|
|
Add a new emoji package button next to the room chat entry in `RoomBottomWidget`.
|
|
|
|
Recommended behavior:
|
|
|
|
- Tap chat entry: open `RoomMsgInput(initialMode: text)`.
|
|
- Tap emoji package button: open `RoomMsgInput(initialMode: emojiPackage)`.
|
|
- If the current user is not on mic, the emoji package button can either be disabled or show a toast such as "Take a mic first".
|
|
|
|
Important layout note:
|
|
|
|
- `RoomBottomWidget` currently uses hard-coded width math for the bottom bar and for `_resolveGiftCenterX(...)`.
|
|
- After adding one more action button, the row layout and gift floating button center calculation both need to be updated together.
|
|
- If only the row is changed and `_resolveGiftCenterX(...)` is not updated, the floating gift button can become visually misaligned.
|
|
|
|
### 2. Composer sheet
|
|
|
|
Recommended sheet structure:
|
|
|
|
- Top area: existing input field, send button, and keyboard/emoji switch logic.
|
|
- Bottom area in `text` mode: current text emoji helper can remain for typing unicode emoji.
|
|
- Bottom area in `emojiPackage` mode: category tabs + emoji package grid from backend data.
|
|
|
|
Recommended send interaction for emoji package:
|
|
|
|
- Tapping an emoji package item sends immediately.
|
|
- The sheet should stay open after sending so the user can send multiple emoji packages continuously.
|
|
- If UI later wants tap-to-close, that can be a product choice, but continuous sending is the safer default for room interaction.
|
|
|
|
### 3. Seat-only display
|
|
|
|
Emoji package messages should not enter room chat lists.
|
|
|
|
Recommended rule:
|
|
|
|
- Text messages still follow the existing room chat path.
|
|
- Emoji package clicks trigger seat animation only.
|
|
- No new item should be inserted into `roomAllMsgList`, `roomChatMsgList`, or `roomGiftMsgList`.
|
|
|
|
## Recommended Send Path
|
|
|
|
### Recommended payload shape
|
|
|
|
The smallest-change plan is to reuse the existing room custom message structure:
|
|
|
|
```dart
|
|
Msg(
|
|
groupId: roomAccount,
|
|
type: SCRoomMsgType.emoticons,
|
|
msg: emoji.sourceUrl,
|
|
number: currentSeatIndex,
|
|
user: currentUserProfile,
|
|
role: currentUserRole,
|
|
)
|
|
```
|
|
|
|
Recommended field meaning:
|
|
|
|
- `type`: `EMOTICONS`
|
|
- `msg`: selected emoji resource URL
|
|
- `number`: current seat index
|
|
- `user`: sender profile, for consistency with existing room messages
|
|
|
|
### Why not send it as a normal chat message
|
|
|
|
Because `RtmProvider.addMsg(...)` always inserts the message into `roomAllMsgList` first.
|
|
|
|
Current receive side is already good enough:
|
|
|
|
- In `_newGroupMsg(...)`, `EMOTICONS` goes directly to `RtcProvider.starPlayEmoji(msg)` and then `return`.
|
|
- That means remote users will not see emoji package content in room chat.
|
|
|
|
Current sender side still needs care during implementation:
|
|
|
|
- If the sender uses `dispatchMessage(...)` with default `addLocal: true`, the local sender will still push one item into `roomAllMsgList`.
|
|
- That would violate the requirement that emoji packages must not show in chat.
|
|
|
|
Recommended implementation behavior later:
|
|
|
|
- Build the `Msg`.
|
|
- First do a local optimistic seat display by calling `RtcProvider.starPlayEmoji(msg)`.
|
|
- Then call `dispatchMessage(msg, addLocal: false)`.
|
|
|
|
This gives the sender instant seat feedback while keeping the chat list clean.
|
|
|
|
## Recommended Seat Resolution Rule
|
|
|
|
When sending an emoji package, determine the seat index at send time, not when the sheet is opened.
|
|
|
|
Recommended lookup:
|
|
|
|
- Use `RtcProvider.userOnMaiInIndex(currentUserId)` when the user taps an emoji item.
|
|
|
|
Reason:
|
|
|
|
- The user may switch mic seats after opening the popup.
|
|
- Using the latest seat index avoids showing the emoji on the wrong seat.
|
|
|
|
If the returned seat index is `-1`:
|
|
|
|
- Do not send.
|
|
- Show a toast telling the user that only users on mic can use emoji packages.
|
|
|
|
## Backend Dependency Assessment
|
|
|
|
### Material data
|
|
|
|
The project already has `/material/emoji/all` and the local model already supports:
|
|
|
|
- category name
|
|
- category cover
|
|
- category-level ownership state
|
|
- category-level amount
|
|
- emoji list
|
|
- emoji cover URL
|
|
- emoji source URL
|
|
|
|
This means the current backend structure is already close to the requirement shown in the design.
|
|
|
|
Backend items to confirm before development:
|
|
|
|
- Whether `/material/emoji/all` is the final interface for this feature
|
|
- Whether category ordering is controlled by backend
|
|
- Whether each category has a tab icon or cover ready for UI
|
|
- Whether `coverUrl` is the correct image for the grid cell
|
|
- Whether `sourceUrl` is the real image used on the mic seat
|
|
- Whether ownership is category-level only or item-level
|
|
|
|
If ownership later becomes item-level, the current model may need extension because it currently looks category-oriented.
|
|
|
|
### No extra send API is strictly required
|
|
|
|
For the minimal-change version, no new HTTP send API is needed.
|
|
|
|
Recommended transport:
|
|
|
|
- Use the existing room RTM custom message channel.
|
|
- Reuse `EMOTICONS` as the message type.
|
|
|
|
This keeps the feature aligned with the current room event architecture.
|
|
|
|
## Optional Sync Decision
|
|
|
|
There are two possible product definitions for emoji package state:
|
|
|
|
### Option A: transient RTM-only state
|
|
|
|
Behavior:
|
|
|
|
- Emoji package is only an instant seat effect.
|
|
- Users who join mid-animation do not need to recover the current emoji state.
|
|
|
|
Pros:
|
|
|
|
- Smallest frontend change
|
|
- No need for backend seat-state persistence
|
|
- Best fit for the current codebase
|
|
|
|
Recommended default:
|
|
|
|
- Yes. This should be the first implementation choice unless product explicitly wants state recovery.
|
|
|
|
### Option B: recoverable seat state
|
|
|
|
Behavior:
|
|
|
|
- If a user joins the room late or reconnects, they can still see the currently active emoji package on the seat during its valid duration.
|
|
|
|
If product wants this, backend and model changes are needed:
|
|
|
|
- mic list response needs to include active emoji state
|
|
- active emoji state should include at least resource URL and expiration timing
|
|
- frontend model parsing needs to be updated
|
|
|
|
Important current gap:
|
|
|
|
- `MicRes.fromJson(...)` does not parse `emojiPath`
|
|
- `MicRes.toJson()` also does not serialize `emojiPath`
|
|
|
|
So if the final backend plan depends on seat polling or room re-entry recovery, this model area will need a small follow-up patch during implementation.
|
|
|
|
## Current Code Caveats To Remember
|
|
|
|
### 1. `RoomMsgInput` is text-first today
|
|
|
|
Current behavior:
|
|
|
|
- It always autofocuses the text field.
|
|
- It only supports the local text emoji helper based on `SCEmojiDatas.smileys`.
|
|
|
|
Meaning for future implementation:
|
|
|
|
- It needs a mode parameter so opening from the emoji package button can skip keyboard autofocus and show the package panel first.
|
|
|
|
### 2. Seat emoji display currently uses an implicit field contract
|
|
|
|
Current behavior in `RtcProvider.starPlayEmoji(msg)`:
|
|
|
|
- It writes `emojiPath: msg.msg`
|
|
- It writes `type: msg.type`
|
|
- It writes `number: msg.msg`
|
|
|
|
Current behavior in `SCSeatItem.Emoticons`:
|
|
|
|
- For dice and RPS, `number` is treated like a result value
|
|
- For other types, `number` is treated like an image URL
|
|
|
|
Meaning:
|
|
|
|
- The current image emoji path works because the code uses a field-reuse convention.
|
|
- Later implementation should stay compatible with this path unless the seat overlay model is cleaned up in one pass.
|
|
|
|
### 3. Emoji cache utility exists but is not wired into the room sheet yet
|
|
|
|
`SCAppGeneralManager` already has:
|
|
|
|
- `emojiByTab`
|
|
- `emojiAll()`
|
|
|
|
But this is not currently connected to the room composer.
|
|
|
|
Recommended later choice:
|
|
|
|
- Either reuse `SCAppGeneralManager` for caching
|
|
- Or load emoji package data directly inside the room sheet and cache locally there
|
|
|
|
Either is feasible. Reusing the manager is better if the same materials are used in multiple room surfaces.
|
|
|
|
## Suggested Development Sequence Later
|
|
|
|
When backend and UI are ready, the development order should be:
|
|
|
|
1. Add the new emoji package button in `RoomBottomWidget` and adjust layout math together with `_resolveGiftCenterX(...)`.
|
|
2. Refactor `RoomMsgInput` into one shared room composer with `text` and `emojiPackage` entry modes.
|
|
3. Wire the sheet to load emoji categories and emoji items from `/material/emoji/all`.
|
|
4. Add on-mic gating and resolve seat index at click time.
|
|
5. On emoji package click, do local optimistic seat playback.
|
|
6. Send RTM `EMOTICONS` with `addLocal: false`.
|
|
7. Verify that `All`, `Chat`, and `Gift` tabs remain unchanged after sending.
|
|
8. If product wants reconnect recovery, then patch `MicRes` parsing and seat sync logic.
|
|
|
|
## Acceptance Checklist
|
|
|
|
- New emoji package button appears next to the room chat entry.
|
|
- Chat entry and emoji package button open the same bottom composer sheet.
|
|
- Opening from chat goes to text mode.
|
|
- Opening from emoji package goes to emoji package mode.
|
|
- Only users on mic can send emoji packages.
|
|
- Sender sees the emoji package on their own seat immediately.
|
|
- Other users see the emoji package on the sender's seat.
|
|
- Emoji package sending does not create new rows in `All`, `Chat`, or `Gift`.
|
|
- Existing text chat sending is not affected.
|
|
- Existing gift floating button remains visually centered after the new button is added.
|
|
- Repeated emoji package taps queue and play correctly on the seat.
|
|
|
|
## Final Recommendation
|
|
|
|
This feature is feasible with low-to-medium implementation risk because the project already has the key building blocks:
|
|
|
|
- shared room composer entry
|
|
- room RTM custom message channel
|
|
- `EMOTICONS` message type
|
|
- seat emoji playback widget
|
|
- emoji material API
|
|
|
|
Recommended final direction:
|
|
|
|
- Reuse the existing room composer popup
|
|
- Reuse `EMOTICONS` as the transport type
|
|
- Use seat-index-based RTM payload
|
|
- Do local optimistic seat playback
|
|
- Do not insert emoji package messages into any chat list
|
|
- Treat backend seat-state recovery as optional, not mandatory
|