fix(tui): render discord mention names
This commit is contained in:
parent
39906edc3d
commit
a4f5d3fdb4
@ -237,14 +237,16 @@ func TestDiscordTUIRowsIncludePaneMetadata(t *testing.T) {
|
||||
AuthorID: "u1",
|
||||
AuthorName: "Peter",
|
||||
Content: "hello from desktop",
|
||||
DisplayContent: "hello from Vincent",
|
||||
CreatedAt: time.Date(2026, 5, 2, 12, 0, 0, 0, time.UTC),
|
||||
ReplyToMessage: "m0",
|
||||
HasAttachments: true,
|
||||
Pinned: true,
|
||||
}})
|
||||
require.Len(t, rows, 1)
|
||||
require.Equal(t, "hello from desktop", rows[0].Title)
|
||||
require.Equal(t, "hello from desktop", rows[0].Detail)
|
||||
require.Equal(t, "hello from Vincent", rows[0].Title)
|
||||
require.Equal(t, "hello from Vincent", rows[0].Detail)
|
||||
require.Equal(t, "hello from Vincent", rows[0].Text)
|
||||
require.Equal(t, "Direct messages", rows[0].Scope)
|
||||
require.Equal(t, "Vincent K", rows[0].Container)
|
||||
require.Contains(t, rows[0].Tags, "dm")
|
||||
|
||||
@ -117,7 +117,8 @@ func (r *runtime) archiveSourceLocation() string {
|
||||
func discordTUIRows(rows []store.MessageRow) []tui.Row {
|
||||
items := make([]tui.Row, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
title := strings.TrimSpace(row.Content)
|
||||
content := discordDisplayContent(row)
|
||||
title := strings.TrimSpace(content)
|
||||
if title == "" {
|
||||
title = row.MessageID
|
||||
}
|
||||
@ -137,8 +138,8 @@ func discordTUIRows(rows []store.MessageRow) []tui.Row {
|
||||
Container: discordContainerLabel(row),
|
||||
Author: discordAuthorLabel(row),
|
||||
Title: title,
|
||||
Text: row.Content,
|
||||
Detail: row.Content,
|
||||
Text: content,
|
||||
Detail: content,
|
||||
URL: discordMessageURL(row),
|
||||
CreatedAt: formatTime(row.CreatedAt),
|
||||
Tags: tags,
|
||||
@ -156,6 +157,13 @@ func discordTUIRows(rows []store.MessageRow) []tui.Row {
|
||||
return items
|
||||
}
|
||||
|
||||
func discordDisplayContent(row store.MessageRow) string {
|
||||
if content := strings.TrimSpace(row.DisplayContent); content != "" {
|
||||
return content
|
||||
}
|
||||
return row.Content
|
||||
}
|
||||
|
||||
func discordMessageURL(row store.MessageRow) string {
|
||||
guildID := strings.TrimSpace(row.GuildID)
|
||||
channelID := strings.TrimSpace(row.ChannelID)
|
||||
|
||||
@ -116,3 +116,44 @@ func TestAttachmentTextAndMentionsAreQueryable(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, filtered, 1)
|
||||
}
|
||||
|
||||
func TestListMessagesResolvesMentionNamesForDisplay(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
s, err := Open(ctx, filepath.Join(t.TempDir(), "discrawl.db"))
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = s.Close() }()
|
||||
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "c1", GuildID: "g1", Kind: "text", Name: "maintainers", RawJSON: `{}`}))
|
||||
|
||||
createdAt := time.Now().UTC().Format(time.RFC3339Nano)
|
||||
rawContent := "ping <@u2> <@!u3> <@&r1> in <#c1>"
|
||||
require.NoError(t, s.UpsertMessages(ctx, []MessageMutation{{
|
||||
Record: MessageRecord{
|
||||
ID: "m1",
|
||||
GuildID: "g1",
|
||||
ChannelID: "c1",
|
||||
ChannelName: "maintainers",
|
||||
AuthorID: "u1",
|
||||
AuthorName: "Peter",
|
||||
MessageType: 0,
|
||||
CreatedAt: createdAt,
|
||||
Content: rawContent,
|
||||
NormalizedContent: rawContent,
|
||||
RawJSON: `{}`,
|
||||
},
|
||||
Mentions: []MentionEventRecord{
|
||||
{MessageID: "m1", GuildID: "g1", ChannelID: "c1", AuthorID: "u1", TargetType: "user", TargetID: "u2", TargetName: "Shadow", EventAt: createdAt},
|
||||
{MessageID: "m1", GuildID: "g1", ChannelID: "c1", AuthorID: "u1", TargetType: "user", TargetID: "u3", TargetName: "Vincent", EventAt: createdAt},
|
||||
{MessageID: "m1", GuildID: "g1", ChannelID: "c1", AuthorID: "u1", TargetType: "role", TargetID: "r1", TargetName: "Maintainers", EventAt: createdAt},
|
||||
{MessageID: "m1", GuildID: "g1", ChannelID: "c1", AuthorID: "u1", TargetType: "channel", TargetID: "c1", TargetName: "maintainers", EventAt: createdAt},
|
||||
},
|
||||
}}))
|
||||
|
||||
messages, err := s.ListMessages(ctx, MessageListOptions{Channel: "maintainers", Limit: 10})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, messages, 1)
|
||||
require.Equal(t, rawContent, messages[0].Content)
|
||||
require.Equal(t, "ping @Shadow @Vincent @Maintainers in #maintainers", messages[0].DisplayContent)
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ type MessageRow struct {
|
||||
AuthorID string `json:"author_id"`
|
||||
AuthorName string `json:"author_name"`
|
||||
Content string `json:"content"`
|
||||
DisplayContent string `json:"display_content,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ReplyToMessage string `json:"reply_to_message_id,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
@ -161,11 +162,72 @@ func (s *Store) ListMessages(ctx context.Context, opts MessageListOptions) ([]Me
|
||||
row.CreatedAt = parseTime(created)
|
||||
row.HasAttachments = hasAttachments == 1
|
||||
row.Pinned = pinned == 1
|
||||
row.DisplayContent = row.Content
|
||||
out = append(out, row)
|
||||
}
|
||||
return out, rows.Err()
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, s.resolveMessageDisplayMentions(ctx, out)
|
||||
}
|
||||
|
||||
func normalizeChannelFilter(raw string) string {
|
||||
return strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(raw), "#"))
|
||||
}
|
||||
|
||||
func (s *Store) resolveMessageDisplayMentions(ctx context.Context, rows []MessageRow) error {
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
ids := make([]any, 0, len(rows))
|
||||
indexByID := make(map[string]int, len(rows))
|
||||
for index, row := range rows {
|
||||
id := strings.TrimSpace(row.MessageID)
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, id)
|
||||
indexByID[id] = index
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
query := `select message_id, target_type, target_id, target_name from mention_events where message_id in (` + placeholders(len(ids)) + `)`
|
||||
mentionRows, err := s.db.QueryContext(ctx, query, ids...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = mentionRows.Close() }()
|
||||
for mentionRows.Next() {
|
||||
var messageID, targetType, targetID, targetName string
|
||||
if err := mentionRows.Scan(&messageID, &targetType, &targetID, &targetName); err != nil {
|
||||
return err
|
||||
}
|
||||
index, ok := indexByID[messageID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rows[index].DisplayContent = replaceDiscordMention(rows[index].DisplayContent, targetType, targetID, targetName)
|
||||
}
|
||||
return mentionRows.Err()
|
||||
}
|
||||
|
||||
func replaceDiscordMention(content, targetType, targetID, targetName string) string {
|
||||
targetID = strings.TrimSpace(targetID)
|
||||
if targetID == "" {
|
||||
return content
|
||||
}
|
||||
label := strings.TrimSpace(targetName)
|
||||
if label == "" {
|
||||
label = targetID
|
||||
}
|
||||
switch strings.TrimSpace(targetType) {
|
||||
case "role":
|
||||
return strings.ReplaceAll(content, "<@&"+targetID+">", "@"+label)
|
||||
case "channel":
|
||||
return strings.ReplaceAll(content, "<#"+targetID+">", "#"+label)
|
||||
default:
|
||||
content = strings.ReplaceAll(content, "<@"+targetID+">", "@"+label)
|
||||
return strings.ReplaceAll(content, "<@!"+targetID+">", "@"+label)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user