Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 2 additions & 44 deletions desktop/src-tauri/src/events/message_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use super::check_pubkey;

const MAX_THREAD_ROOT_EXCERPT_CHARS: usize = 64;
const SENT_FROM_THREAD_TAG: &str = "buzz:sent-from-thread";
const AGENT_ADDRESS_MENTION_MARKER: &str = "agent-address";

pub(super) fn mention_reference_tags(
mentions: &[Vec<String>],
Expand All @@ -20,20 +19,10 @@ pub(super) fn mention_reference_tags(
let Some(pubkey) = mention.get(1) else {
return Err("mention reference tag missing pubkey".into());
};
if mention.len() > 3
|| (mention.len() == 3
&& mention.get(2).map(String::as_str) != Some(AGENT_ADDRESS_MENTION_MARKER))
{
return Err("mention reference tag has invalid display metadata".into());
}
check_pubkey(pubkey)?;
let normalized_pubkey = pubkey.to_ascii_lowercase();
let mut parts = vec!["mention", normalized_pubkey.as_str()];
if mention.len() == 3 {
parts.push(AGENT_ADDRESS_MENTION_MARKER);
}
tags.push(
Tag::parse(parts).map_err(|error| format!("invalid mention reference tag: {error}"))?,
Tag::parse(vec!["mention", &pubkey.to_ascii_lowercase()])
.map_err(|error| format!("invalid mention reference tag: {error}"))?,
);
}
Ok(())
Expand Down Expand Up @@ -126,39 +115,8 @@ pub(super) fn append_client_tags(
mod tests {
use super::*;

const PUBKEY: &str = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
const ROOT_HEX: &str = "d24da132115ca0a46233cf4c2ad8338fbf914250cbcaa9181a6dd59533cb5ac1";

#[test]
fn mention_reference_preserves_agent_address_display_metadata() {
let mut tags = Vec::new();
mention_reference_tags(
&[vec![
"mention".into(),
PUBKEY.to_ascii_uppercase(),
AGENT_ADDRESS_MENTION_MARKER.into(),
]],
&mut tags,
)
.unwrap();

assert_eq!(
tags[0].as_slice(),
&["mention", PUBKEY, AGENT_ADDRESS_MENTION_MARKER]
);
}

#[test]
fn mention_reference_rejects_unknown_display_metadata() {
let mut tags = Vec::new();
let result = mention_reference_tags(
&[vec!["mention".into(), PUBKEY.into(), "unknown".into()]],
&mut tags,
);

assert!(result.is_err());
}

#[test]
fn message_accepts_only_valid_sent_from_thread_provenance() {
let source_tag = vec![
Expand Down
21 changes: 10 additions & 11 deletions desktop/src/features/channels/ui/ChannelPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ export const ChannelPane = React.memo(function ChannelPane({
}),
[activeChannel, currentPubkey, profiles],
);

const handleWelcomeAddAgent = React.useCallback(() => {
onAddAgent?.({
beforeSend: () =>
Expand All @@ -373,14 +374,13 @@ export const ChannelPane = React.memo(function ChannelPane({
onWelcomeAddAgent: onAddAgent ? handleWelcomeAddAgent : undefined,
});
const channelIntro = isHuddleTranscript ? null : standardChannelIntro;
const { mainTimelineEntries, recentMentions, visibleMessages } =
useChannelPaneMessages({
activeChannel,
isHuddleTranscript,
messages,
profiles,
threadSummaries,
});
const { mainTimelineEntries, visibleMessages } = useChannelPaneMessages({
activeChannel,
isHuddleTranscript,
messages,
profiles,
threadSummaries,
});
useRenderScopedReactionHydration({
activeChannel,
mainTimelineEntries,
Expand Down Expand Up @@ -753,7 +753,6 @@ export const ChannelPane = React.memo(function ChannelPane({
) : null}
<ComposerDockBackdrop gutterClassName="inset-x-5" />
<MessageComposer
audienceContext={{ type: "channel" }}
channelId={activeChannel?.id ?? null}
channelName={activeChannel?.name ?? "channel"}
channelType={activeChannel?.channelType ?? null}
Expand All @@ -775,7 +774,7 @@ export const ChannelPane = React.memo(function ChannelPane({
: undefined
}
onSend={handleSendMessage}
{...{ profiles, recentMentionPubkeys: recentMentions }}
profiles={profiles}
showBackgroundUploadProgress={false}
placeholder={
timeoutState.active
Expand Down Expand Up @@ -877,7 +876,7 @@ export const ChannelPane = React.memo(function ChannelPane({
onScrollTargetSettled={resolveScrollTarget}
onToggleReaction={onToggleReaction}
onUnfollowThread={onUnfollowThread}
{...{ profiles, recentMentionPubkeys: recentMentions }}
profiles={profiles}
replyTargetMessage={threadReplyTargetMessage}
scrollTargetHighlights={!layoutScrollTargetId}
scrollTargetId={layoutScrollTargetId ?? threadScrollTargetId}
Expand Down
12 changes: 1 addition & 11 deletions desktop/src/features/channels/ui/useChannelPaneMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
} from "@/features/channels/ui/ChannelPane.helpers";
import type { ChannelPaneProps } from "@/features/channels/ui/ChannelPane.types";
import { buildMainTimelineEntries } from "@/features/messages/lib/threadPanel";
import { getRecentMentionPubkeys } from "@/features/messages/lib/recentMentionPubkeys";
import { isWelcomeExperienceChannel } from "@/features/onboarding/welcome";

type ChannelPaneMessagesOptions = Pick<
Expand Down Expand Up @@ -47,14 +46,5 @@ export function useChannelPaneMessages({
[isHuddleTranscript, profiles, threadSummaries, visibleMessages],
);

const recentMentionPubkeys = React.useMemo(
() => getRecentMentionPubkeys(messages, activeChannel?.channelType),
[activeChannel?.channelType, messages],
);

return {
mainTimelineEntries,
recentMentions: recentMentionPubkeys,
visibleMessages,
};
return { mainTimelineEntries, visibleMessages };
}
2 changes: 0 additions & 2 deletions desktop/src/features/communities/useCommunityInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
import { resetRenderScopedReactionHydration } from "@/features/messages/lib/renderScopedReactions";
import { resetBackgroundMediaUploads } from "@/features/messages/lib/backgroundMediaUploadStore";
import { resetLinkPreviewPreparations } from "@/features/messages/lib/linkPreviewPreparationStore";
import { resetPersistentAgentAudienceStore } from "@/features/messages/lib/persistentAgentAudience";
import {
resetActiveAgentTurnsStore,
saveActiveAgentTurnsForCommunity,
Expand Down Expand Up @@ -77,7 +76,6 @@ async function resetCommunityState({
resetRenderScopedReactionHydration();
resetBackgroundMediaUploads();
resetLinkPreviewPreparations();
resetPersistentAgentAudienceStore();
clearSearchHitEventCache();
clearMarkdownNodeCache();
resetMessageLinkMetadataCache();
Expand Down
1 change: 0 additions & 1 deletion desktop/src/features/forum/ui/ForumComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,6 @@ export function ForumComposer({
mentions.isMentionOpen ? mentions.suggestions : []
}
onChannelSelect={applyChannelInsert}
onMentionDismiss={mentions.cancelMentionAutocomplete}
onMentionFetchMore={mentions.fetchMoreSuggestions}
onMentionSelect={applyMentionInsert}
position={autocompletePosition}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ type ForumComposerAutocompletesProps = {
mentionSuggestions: MentionSuggestion[];
onChannelSelect: (suggestion: ChannelSuggestion) => void;
onMentionFetchMore?: () => void;
onMentionDismiss: () => void;
onMentionSelect: (suggestion: MentionSuggestion) => void;
position: "above" | "below";
};
Expand All @@ -24,7 +23,6 @@ export function ForumComposerAutocompletes({
mentionSuggestions,
onChannelSelect,
onMentionFetchMore,
onMentionDismiss,
onMentionSelect,
position,
}: ForumComposerAutocompletesProps) {
Expand All @@ -37,7 +35,6 @@ export function ForumComposerAutocompletes({
suggestions={channelSuggestions}
/>
<MentionAutocomplete
onDismiss={onMentionDismiss}
onFetchMore={onMentionFetchMore}
onSelect={onMentionSelect}
position={position}
Expand Down
47 changes: 46 additions & 1 deletion desktop/src/features/home/ui/InboxDetailPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
isWithinGroupingWindow,
startsNewMessageGroup,
} from "@/features/messages/lib/messageGrouping";
import { orderMentionPubkeysByText } from "@/features/messages/lib/orderMentionPubkeys";
import { canManageMessageForCurrentUser } from "@/features/messages/lib/canManageMessage";
import { buildEditMentionState } from "@/features/messages/lib/draftMentionRefs";
import { imetaMediaFromTags } from "@/features/messages/lib/imetaMediaMarkdown";
Expand All @@ -42,11 +43,13 @@ import {
hasRenderedVideoAttachment,
} from "@/features/messages/lib/videoReviewContext";
import { getThreadReference } from "@/features/messages/lib/threading";
import { normalizePubkey } from "@/shared/lib/pubkey";
import { MessageComposer } from "@/features/messages/ui/MessageComposer";
import { useAnchoredScroll } from "@/features/messages/ui/useAnchoredScroll";
import { useComposerHeightPadding } from "@/features/messages/ui/useComposerHeightPadding";
import { UpdateIndicator } from "@/features/settings/UpdateIndicator";
import type { Channel, UserProfileSummary } from "@/shared/api/types";
import { resolveMentionProps } from "@/shared/lib/resolveMentionNames";
import { TopChromeInsetHeader } from "@/shared/layout/TopChromeInsetHeader";
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
Expand Down Expand Up @@ -217,6 +220,40 @@ function InboxMessageDetailPane({
// Build the plain, non-virtualized timeline the shared hook anchors against.
// Live arrivals rerun its layout compensation without changing the target.

// A latest reply can represent an Inbox conversation. Resolve the actual
// root from loaded context or the complete feed group; never treat an
// unresolved root/profile lookup as an authoritative empty audience.
const contextRoot = messages.find((message) => message.id === conversationId);
const feedRoot = item
? [item.item, ...item.groupItems].find(
(groupItem) => groupItem.id === conversationId,
)
: undefined;
const rootMessage = contextRoot
? {
authorPubkey: contextRoot.authorPubkey,
content: contextRoot.content,
mentionPubkeysByName: contextRoot.mentionPubkeysByName,
}
: feedRoot && profiles
? {
authorPubkey: feedRoot.pubkey,
content: feedRoot.content,
mentionPubkeysByName: resolveMentionProps(feedRoot.tags, profiles)
.mentionPubkeysByName,
}
: null;
const initialAgentPubkeys = rootMessage
? currentPubkey &&
normalizePubkey(rootMessage.authorPubkey) ===
normalizePubkey(currentPubkey)
? orderMentionPubkeysByText(
rootMessage.content,
rootMessage.mentionPubkeysByName,
(pubkey) => agentPubkeys?.has(pubkey) === true,
)
: []
: undefined;
const displayMessages = React.useMemo<InboxDisplayMessage[]>(() => {
const selectedMessage = messages.find((message) => message.isSelected);
const pendingReplyMessages: InboxDisplayMessage[] = replies.map(
Expand Down Expand Up @@ -804,7 +841,15 @@ function InboxMessageDetailPane({
/>
<div className="pointer-events-auto">
<MessageComposer
audienceContext={isDirectMessage ? null : { type: "thread" }}
audienceContext={
isDirectMessage
? null
: {
type: "thread",
threadRootId: item.conversationId,
initialAgentPubkeys,
}
}
channelId={item.item.channelId}
channelName={item.channelLabel ?? "channel"}
channelType={composerChannelType}
Expand Down
12 changes: 0 additions & 12 deletions desktop/src/features/messages/lib/agentAddressMention.d.mts

This file was deleted.

39 changes: 0 additions & 39 deletions desktop/src/features/messages/lib/agentAddressMention.mjs

This file was deleted.

30 changes: 0 additions & 30 deletions desktop/src/features/messages/lib/agentAddressMention.test.mjs

This file was deleted.

Loading
Loading