diff --git a/src/components/inbox/message-bubble.tsx b/src/components/inbox/message-bubble.tsx index a98beb2..54dbac3 100644 --- a/src/components/inbox/message-bubble.tsx +++ b/src/components/inbox/message-bubble.tsx @@ -17,6 +17,8 @@ import { import { format } from "date-fns"; import { ReplyQuote } from "./reply-quote"; import { MessageReactions } from "./message-reactions"; +import { useAuth } from "@/hooks/use-auth"; +import { isFlowsEnabled } from "@/lib/flows/feature-flag"; interface MessageBubbleProps { message: Message; @@ -117,6 +119,14 @@ function MediaImage({ url, alt }: { url: string; alt: string }) { } function MessageContent({ message }: { message: Message }) { + // Used to gate the interactive-reply bubble style below. The webhook + // only ever inserts `content_type='interactive'` rows when our runner + // sent an interactive message first — and the runner itself is behind + // the same beta gate (forthcoming PRs) — so this gate is belt-and- + // suspenders against any stray row that slipped in via manual SQL, + // a misconfigured import, or a future bug. Non-beta accounts fall + // back to plain text. + const { profile } = useAuth(); switch (message.content_type) { case "text": return ( @@ -213,7 +223,19 @@ function MessageContent({ message }: { message: Message }) { ); - case "interactive": + case "interactive": { + // Defensive gate — non-beta accounts render the tapped option as + // plain text. See the comment at the top of MessageContent for why + // this can't be reached in normal operation; this branch exists so + // that if a stray interactive row ever appears for a non-beta + // account, they don't see UI for a feature they haven't opted into. + if (!isFlowsEnabled(profile)) { + return ( +

+ {message.content_text || "[Interactive reply]"} +

+ ); + } // Customer tapped a reply button or list row on a message the bot // sent. We show the tapped option's title (already in content_text, // set by parseMessageContent in the webhook) with a small affordance @@ -230,6 +252,7 @@ function MessageContent({ message }: { message: Message }) {

); + } default: return (