Skip to content

Commit e24f341

Browse files
properly resolve account usernames when replying to posts directly from remote instances' timelines
1 parent d30c9d0 commit e24f341

3 files changed

Lines changed: 31 additions & 16 deletions

File tree

src/commands.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,11 +1312,11 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
13121312
if let Some(reblog) = &status.reblog {
13131313
push_unique(suggestions, format!("@{}", reblog.account.full_acct()));
13141314
for mention in &reblog.mentions {
1315-
push_unique(suggestions, format!("@{}", mention.acct));
1315+
push_unique(suggestions, format!("@{}", mention.full_acct()));
13161316
}
13171317
}
13181318
for mention in &status.mentions {
1319-
push_unique(suggestions, format!("@{}", mention.acct));
1319+
push_unique(suggestions, format!("@{}", mention.full_acct()));
13201320
}
13211321
};
13221322

@@ -1440,24 +1440,22 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
14401440
return;
14411441
}
14421442

1443-
// Resolution chain: get_account by ID → lookup_account by acct → synthetic fallback
14441443
let account = if let (Some(client), Some(token)) = (&state.client, &state.access_token) {
14451444
let by_id = if mention.id.is_empty() { None } else { client.get_account(token, &mention.id).ok() };
1446-
by_id.or_else(|| client.lookup_account(token, &mention.acct).ok())
1445+
by_id.or_else(|| client.lookup_account(token, &mention.full_acct()).ok())
14471446
} else {
14481447
None
14491448
};
14501449
let account = match account {
14511450
Some(acc) => acc,
14521451
None if mention.id.is_empty() => {
1453-
// HTML-derived mention: no local ID and lookup failed — nothing we can do
1454-
live_region.announce(&format!("Could not resolve account @{}", mention.acct));
1452+
live_region.announce(&format!("Could not resolve account @{}", mention.full_acct()));
14551453
return;
14561454
}
14571455
None => crate::mastodon::Account {
14581456
id: mention.id.clone(),
14591457
username: mention.username.clone(),
1460-
acct: mention.acct.clone(),
1458+
acct: mention.full_acct(),
14611459
display_name: String::new(),
14621460
url: mention.url,
14631461
note: String::new(),
@@ -1635,11 +1633,11 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
16351633
all_mentions.push(crate::mastodon::Mention { id: String::new(), username, acct, url });
16361634
}
16371635
for mention in all_mentions {
1638-
if !all_users.iter().any(|u| u.acct == mention.acct) {
1636+
if !all_users.iter().any(|u| u.acct == mention.full_acct()) {
16391637
all_users.push(crate::mastodon::Account {
16401638
id: mention.id.clone(),
16411639
username: mention.username.clone(),
1642-
acct: mention.acct.clone(),
1640+
acct: mention.full_acct(),
16431641
display_name: String::new(),
16441642
url: mention.url,
16451643
note: String::new(),

src/mastodon.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,21 @@ pub struct Mention {
252252
pub url: String,
253253
}
254254

255+
impl Mention {
256+
pub fn full_acct(&self) -> String {
257+
if self.acct.contains('@') {
258+
self.acct.clone()
259+
} else {
260+
if let Ok(url) = reqwest::Url::parse(&self.url) {
261+
if let Some(host) = url.host_str() {
262+
return format!("{}@{}", self.acct, host);
263+
}
264+
}
265+
self.acct.clone()
266+
}
267+
}
268+
}
269+
255270
#[derive(Debug, Clone, Deserialize)]
256271
#[allow(dead_code)]
257272
pub struct Tag {

src/ui/dialogs/compose.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,25 +1166,26 @@ pub fn prompt_for_reply(
11661166
let mut accts = Vec::new();
11671167
let self_acct = self_acct.map(|acct| acct.trim().trim_start_matches('@')).filter(|acct| !acct.is_empty());
11681168
if let Some(self_acct) = self_acct {
1169-
if !self_acct.eq_ignore_ascii_case(replying_to.account.acct.trim().trim_start_matches('@')) {
1170-
accts.push(replying_to.account.acct.clone());
1169+
if !self_acct.eq_ignore_ascii_case(replying_to.account.full_acct().trim().trim_start_matches('@')) {
1170+
accts.push(replying_to.account.full_acct());
11711171
}
11721172
} else {
1173-
accts.push(replying_to.account.acct.clone());
1173+
accts.push(replying_to.account.full_acct());
11741174
}
11751175
for m in &replying_to.mentions {
11761176
if let Some(self_acct) = self_acct
11771177
&& is_self_mention(self_acct, m)
11781178
{
11791179
continue;
11801180
}
1181-
if !accts.iter().any(|a| a == &m.acct) {
1182-
accts.push(m.acct.clone());
1181+
let m_full_acct = m.full_acct();
1182+
if !accts.iter().any(|a| a == &m_full_acct) {
1183+
accts.push(m_full_acct);
11831184
}
11841185
}
11851186
accts.iter().map(|a| format!("@{a}")).collect::<Vec<_>>().join(" ") + " "
11861187
} else {
1187-
format!("@{} ", replying_to.account.acct)
1188+
format!("@{} ", replying_to.account.full_acct())
11881189
};
11891190
let default_visibility = match replying_to.visibility.as_str() {
11901191
"unlisted" => PostVisibility::Unlisted,
@@ -1309,7 +1310,8 @@ pub fn prompt_for_quote(
13091310
}
13101311

13111312
fn is_self_mention(self_acct: &str, mention: &crate::mastodon::Mention) -> bool {
1312-
let mention_acct = mention.acct.trim().trim_start_matches('@');
1313+
let mention_acct = mention.full_acct();
1314+
let mention_acct = mention_acct.trim().trim_start_matches('@');
13131315
if self_acct.eq_ignore_ascii_case(mention_acct) {
13141316
return true;
13151317
}

0 commit comments

Comments
 (0)