fix(composer): restore card mint status in activity rail

Render the existing card-mint status chip inside the unified composer activity row so minting, ready, and failure states remain visible alongside live agent pills and typing.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-05 16:42:03 -07:00
committed by Carl
parent 0d98a40a78
commit b0d2b64094
2 changed files with 3 additions and 17 deletions
@@ -9,6 +9,7 @@ import {
getAgentTranscript,
subscribeAgentObserverStore,
} from "@/features/agents/observerRelayStore";
import { CardMintComposerChip } from "@/features/agents/ui/CardMintComposerChip";
import {
BotActivityComposerAction,
type BotActivityAgent,
@@ -136,7 +137,8 @@ export function ChannelComposerActivityRow({
className="h-8.5 overflow-visible bg-background px-5 pb-1.5 pt-0"
data-testid="channel-composer-activity-row"
>
<div className="flex h-full w-full items-center overflow-visible">
<div className="flex h-full w-full items-center gap-2 overflow-visible">
<CardMintComposerChip />
{/* One strip hosts both groups: working pills plus the typing group
as the strip's trailing slot sibling, so they share the scroller,
edge fades, and layout/enter/exit animations. When the row gets
@@ -37,7 +37,6 @@ import { getThreadPanelLayout } from "@/features/channels/lib/threadPanelLayout"
import { useThreadViewMode } from "@/features/channels/lib/threadViewModePreference";
import { useThreadViewModeSwitch } from "@/features/channels/ui/useThreadViewModeSwitch";
import { useFocusDrawerPresence } from "@/features/channels/ui/useFocusDrawerPresence";
import { useChannelWorkingAgentPubkeys } from "@/features/agents/agentWorkingSignal";
import { useCardMintJobs } from "@/features/agents/cardMintStore";
import { BotActivityComposerAction } from "@/features/channels/ui/BotActivityBar";
import { ChannelComposerActivityRow } from "@/features/channels/ui/ChannelComposerActivityRow";
@@ -287,7 +286,6 @@ export const ChannelPane = React.memo(function ChannelPane({
onEdit(target);
return true;
}, [findLastOwnEditable, messages, onEdit]);
const handleEditLastOwnThreadMessage = React.useCallback((): boolean => {
if (!onEdit) return false;
const scope: TimelineMessage[] = [];
@@ -298,9 +296,7 @@ export const ChannelPane = React.memo(function ChannelPane({
onEdit(target);
return true;
}, [findLastOwnEditable, onEdit, threadHeadMessage, threadMessages]);
const timeoutState = useTimeoutState();
// A moderation DM (1:1 with the relay identity) is read-only for the member;
// only DMs pay for the NIP-11 `self` lookup. Fails open: no `relaySelf` →
// ordinary DM, composer enabled.
@@ -310,7 +306,6 @@ export const ChannelPane = React.memo(function ChannelPane({
currentPubkey,
relaySelfQuery.data,
);
const isComposerDisabled =
!activeChannel?.isMember ||
activeChannel.archivedAt !== null ||
@@ -320,7 +315,6 @@ export const ChannelPane = React.memo(function ChannelPane({
isSending;
const knownAgentPubkeys = React.useMemo(() => {
const pubkeys = new Set<string>();
for (const pubkey of agentPubkeys ?? []) {
pubkeys.add(pubkey.toLowerCase());
}
@@ -330,14 +324,12 @@ export const ChannelPane = React.memo(function ChannelPane({
for (const agent of activityAgents) {
pubkeys.add(agent.pubkey.toLowerCase());
}
return pubkeys;
}, [activityAgents, agentPubkeys, agentSessionAgents]);
const completeWelcomeComposerBanner = React.useCallback(() => {
if (!activeChannelId || !isActiveWelcomeChannel) {
return;
}
clearWelcomeComposerDismissTimer();
completedWelcomeBannerChannelIdsRef.current.add(activeChannelId);
setWelcomeComposerBannerState("complete");
@@ -369,10 +361,8 @@ export const ChannelPane = React.memo(function ChannelPane({
isActiveWelcomeChannel &&
(containsWelcomePersonaMention(content) ||
mentionsKnownAgent(mentionPubkeys, knownAgentPubkeys));
messageTimelineRef.current?.scrollToBottomOnNextUpdate();
await onSendMessage(content, mentionPubkeys, mediaTags, channelId);
if (
channelId &&
channelId !== activeChannelId &&
@@ -381,7 +371,6 @@ export const ChannelPane = React.memo(function ChannelPane({
) {
await goChannel(channelId, { replace: true });
}
if (shouldCompleteWelcomeBanner) {
completeWelcomeComposerBanner();
}
@@ -435,7 +424,6 @@ export const ChannelPane = React.memo(function ChannelPane({
}),
[activeChannel, currentPubkey, profiles],
);
const handleWelcomeAddAgent = React.useCallback(() => {
onAddAgent?.({
beforeSend: () =>
@@ -477,7 +465,6 @@ export const ChannelPane = React.memo(function ChannelPane({
for (const message of threadAllMessages) {
messagesById.set(message.id, message);
}
return buildVideoReviewContextsByMessageId({
channelId: activeChannel?.id ?? null,
channelName: activeChannel?.name,
@@ -498,7 +485,6 @@ export const ChannelPane = React.memo(function ChannelPane({
threadAllMessages,
threadHeadMessage,
]);
const isOverlay = useIsThreadPanelOverlay();
const useSplitAuxiliaryPane = !isSinglePanelView && !isOverlay;
const threadViewMode = useThreadViewMode();
@@ -591,7 +577,6 @@ export const ChannelPane = React.memo(function ChannelPane({
data-testid="channel-shared-header-backdrop"
/>
) : null}
{!isSinglePanelView ? (
<section
aria-label="Channel messages and composer"
@@ -817,7 +802,6 @@ export const ChannelPane = React.memo(function ChannelPane({
) : null}
</section>
) : null}
{/*
* `AnimatePresence` keeps the focus thread drawer mounted through its exit
* animation — without it the drawer's own existence condition