Development notes

Messages module features

What the current Messages inbox already does, taken from src/app/modules/messages. This is the baseline for a revamp, not a proposal.

What the module is

One lazy-loaded route at /messages that renders MessageContainerComponent. Creators and fans use the same page. The left column is the conversation list. The right column is the thread and composer. Creators also get a second top tab for automated messages.

Leaving the page is blocked while an upload queue is still in flight. The route provides AudioRecordingService for in-thread voice notes.

Inbox and conversation list

Creators switch between three list tabs. Fans only see their 1:1 threads. Counts sit on the Priority and General tabs.

Tab Who sees it What it holds
Priority Creators 1:1 threads with featured or subscribed fans. Online and offline badges show here.
General Creators and fans Ordinary 1:1 threads. Fans only have this list.
Message Lists Creators Broadcast lists. Each row shows a list name, optional icon, and recipient count.
  • Search filters the open list as the user types.
  • Rows show last-message preview, relative time, unread count (caps at 99+), verification tick, subscriber mark, and fan badges.
  • A tip icon marks an unread tip. A refund icon marks a pending guaranteed reply.
  • Last-message preview collapses attachments to the word Media and strips URLs from text.
  • On phones, picking a thread hides the list. A close control on the thread header brings the list back.
  • Empty lists say No Messages Found.

Starting a conversation

Adult users on TopFan, or the white-label owner on a branded site, get + New Message. On the Message Lists tab that button becomes + New Message List.

  • A 1:1 start is a search plus a single radio pick, then Select Recipient.
  • A list start is a two-step modal. Step 1 picks recipients. Step 2 names the list (max 25 characters) and optionally crops a round icon.
  • Step 1 can add existing lists or individual people, not both at once. Selected names show as chips.
  • Prebuilt audience keys include All Followers and Subscribers, Current Followers, All Current Subscribers, Previously Paid Subscribers, Top 20 Spenders, and Prior Paid DM Users. The UI relabels the first two to Fans and Subscribers and Current Fans.
  • From Message Lists, a row menu can edit details, edit recipients, or delete the list. Delete asks for confirmation.
  • The recipients modal splits current vs suggested people, searches both, and refuses a list with nobody left.
  • Deep links open a specific person or list from ?user=, ?id=, or router state such as user_key and groupId. A tip-back path from wallet can land on the matching thread.

Thread header and history

  • The header shows the other person's name, photo, badges, and verification. Featured 1:1 threads also show Online or Offline.
  • Clicking a 1:1 avatar opens that profile. Broadcast headers stay inert. List threads show recipient count and open the recipients editor.
  • On TopFan, a menu on the list row and the featured thread header can block the other person. Report exists in code but is not wired in the template.
  • History loads oldest-first as the user scrolls up. Day labels split the thread. Each bubble has a time stamp and the sender photo. Broadcast bubbles use a distinct avatar treatment.
  • Incoming and outgoing bubbles sit on opposite sides. Links in text become clickable.

Message types in the thread

Type What the receiver sees
Text Plain copy, including first-name tokens on list sends.
Image WebP preview. Tap opens a lightbox with prev and next. Receivers can download.
Video Poster with play. Playback uses the IVS player. Receivers can download.
Audio Inline player with duration. Locked audio shows a placeholder icon instead of a player.
File Name and size. Receivers can download.
Store share Merchandise card. Tap goes to the store item.
Portfolio share Portfolio item card. Tap goes to the item.
Event ticket share Event name, venue, date, timezone, and a View Ticket action.
Tip Amount in the sender's currency plus a Tip label.
Guaranteed reply Amount plus a refund icon and Guaranteed Reply label. Fulfillment can pop a wallet-credit notice.
Order dispute Product image, name, price, and the dispute reason.
Paid lock Blurred or placeholder media, a Paid header with price, optional free preview above, and Unlock. Unlock sends the fan to checkout and back to this thread.

Uploads show a circular percent while they sit in the send queue. Senders see their own locked media unlocked. Currency-aware prices prefer the converted amount when one exists.

Composer

Adults get a textarea, send, and a row of tools. Enter sends. The box grows with the text. Send stays disabled until there is text, media, or a share card.

  • Emoji inserts at the caret from an Apple emoji picker. Clicking outside closes it.
  • Camera attaches images, video, audio, or files. Native apps go through the mobile file bridge. Video over 512 MB is rejected. Images can be compressed before upload.
  • Mic records a voice note. A waveform and timer run while recording. Start, stop, cancel, and a preview player are all there. White-label waveform color follows the tenant button text color.
  • Creators can insert a store item, a portfolio item, or an event ticket. Those pickers are disabled while a tip popup is open.
  • On Message Lists, + Insert First Name drops a first-name token into the draft.
  • People under 18 see a blocked composer and a support link instead of the tools.

Paid attachments

Creators with the lock-message permission can price attachments on Priority, General, Message Lists, and some restricted-DM threads. The price control stays off until at least one attachment is present. Tips and share cards cannot mix with a lock price.

  • The price modal is Price to Unlock Direct Message Attachments. Allowed range is $1 to $10,000.
  • The older modal can open a currency conversion sheet so the same lock has per-currency prices.
  • Automated-message attachments use a slimmer price modal without conversion.
  • Up to 5 paid items and 1 free preview. A priced message must have at least one paid item.
  • Paid items and the preview sit in two drag lists. Dragging a paid item into the preview makes it the teaser. Store, portfolio, and ticket cards cannot become the free preview.
  • The preview can also be a fresh image upload. Clearing the price drops both lists back to ordinary attachments.

Tips, credits, and paid DMs

Fans messaging a creator can attach a tip. Creators who offer guaranteed replies get a stronger path. The tip popup has preset amounts plus a custom field, all in the fan's currency.

  • A guaranteed-reply offer shows a minimum-tip badge. Sending below that amount, or sending with no tip, opens a modal that explains the 7-day refund. The fan can add a tip or send without one.
  • Delegates skip the guaranteed-reply prompt and send without a tip.
  • When the creator charges for DMs, the composer shows remaining free messages, unlimited free messages, or the pay-per-message price after credits run out.
  • A send with leftover credits calls consumeDMCredits. A send with no credits and no tip goes through pay-for-message checkout. A send with no credits plus a tip goes through pay-for-message-and-tip. A send with credits plus a tip goes through the tip payment path.
  • Some creators require a subscription before a fan can keep messaging. That path opens the creator's plan picker.

Automated messages

Creators with the automated-messages permission, and white-label host owners, get a second page tab. It is a table of event type plus body, with edit and delete. An empty state says no automated messages exist yet.

Each event type can be used once. The setup modal takes an event, body text (required, max 1000 characters, can insert first name), and the same paid-media composer used in the inbox.

Event When it fires
Fan Registers A fan follows or registers. Value is FOLLOW_MESSAGE.
Fan Purchases Subscription A fan buys a subscription. Value is SUBSCRIPTION_PURCHASE_MESSAGE.
Fan Purchases Content Post A fan buys a post. Value is CONTENT_PURCHASE_MESSAGE.
Fan's Birthday The fan's birthday. Value is BIRTHDAY_WISH_MESSAGE.

Realtime, uploads, and safety

  • Live delivery goes through ChatService. Private messages append to the open thread. A members payload drives online dots. Presence refreshes on a 2-minute idle timer.
  • 1:1 sends use sendPrivateAndSave. List sends use sendPublic against the list id.
  • Media goes to S3 with progress, then the message is sent. Restricted uploads open the restricted-content carousel instead of sending.
  • White-label exclusive creators who are hidden from TopFan show a lock pane instead of the composer.
  • The Messages tab itself is permission-gated. Lock pricing and automated messages have their own permission keys.
  • The footer hides on this page. The header stays visible.

Hard limits worth keeping in a revamp

Limit Value
Paid attachments per message 5
Free preview items 1
Video size 512 MB
Attachment price $1 to $10,000
Automated message text 1000 characters
Message list name 25 characters
Minimum sender age 18
Unread badge cap 99+