Link hashtags in posts to corresponding tag timeline pages
This commit is contained in:
parent
da918d2296
commit
816cae6b78
5 changed files with 27 additions and 13 deletions
|
@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::frontend::get_tag_page_url;
|
||||||
use crate::models::posts::types::Post;
|
use crate::models::posts::types::Post;
|
||||||
use crate::models::profiles::types::DbActorProfile;
|
use crate::models::profiles::types::DbActorProfile;
|
||||||
use crate::models::users::types::User;
|
use crate::models::users::types::User;
|
||||||
|
@ -186,10 +187,11 @@ pub fn create_note(
|
||||||
tags.push(tag);
|
tags.push(tag);
|
||||||
};
|
};
|
||||||
for tag_name in &post.tags {
|
for tag_name in &post.tags {
|
||||||
|
let tag_page_url = get_tag_page_url(instance_url, tag_name);
|
||||||
let tag = Tag {
|
let tag = Tag {
|
||||||
name: format!("#{}", tag_name),
|
name: format!("#{}", tag_name),
|
||||||
tag_type: HASHTAG.to_string(),
|
tag_type: HASHTAG.to_string(),
|
||||||
href: None,
|
href: Some(tag_page_url),
|
||||||
};
|
};
|
||||||
tags.push(tag);
|
tags.push(tag);
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,7 +71,7 @@ async fn actor_view(
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let user = get_user_by_name(db_client, &username).await?;
|
let user = get_user_by_name(db_client, &username).await?;
|
||||||
if !is_activitypub_request(&request) {
|
if !is_activitypub_request(&request) {
|
||||||
let page_url = get_profile_page_url(&user.id, &config.instance_url());
|
let page_url = get_profile_page_url(&config.instance_url(), &user.id);
|
||||||
let response = HttpResponse::Found()
|
let response = HttpResponse::Found()
|
||||||
.header("Location", page_url)
|
.header("Location", page_url)
|
||||||
.finish();
|
.finish();
|
||||||
|
@ -203,7 +203,7 @@ pub async fn object_view(
|
||||||
.find(|post| post.id == internal_object_id && post.author.is_local())
|
.find(|post| post.id == internal_object_id && post.author.is_local())
|
||||||
.ok_or(HttpError::NotFoundError("post"))?;
|
.ok_or(HttpError::NotFoundError("post"))?;
|
||||||
if !is_activitypub_request(&request) {
|
if !is_activitypub_request(&request) {
|
||||||
let page_url = get_post_page_url(&post.id, &config.instance_url());
|
let page_url = get_post_page_url(&config.instance_url(), &post.id);
|
||||||
let response = HttpResponse::Found()
|
let response = HttpResponse::Found()
|
||||||
.header("Location", page_url)
|
.header("Location", page_url)
|
||||||
.finish();
|
.finish();
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
// Assuming frontend is on the same host as backend
|
// Assuming frontend is on the same host as backend
|
||||||
pub fn get_profile_page_url(profile_id: &Uuid, instance_url: &str) -> String {
|
pub fn get_profile_page_url(instance_url: &str, profile_id: &Uuid) -> String {
|
||||||
format!("{}/profile/{}", instance_url, profile_id)
|
format!("{}/profile/{}", instance_url, profile_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_post_page_url(post_id: &Uuid, instance_url: &str) -> String {
|
pub fn get_post_page_url(instance_url: &str, post_id: &Uuid) -> String {
|
||||||
format!("{}/post/{}", instance_url, post_id)
|
format!("{}/post/{}", instance_url, post_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_tag_page_url(instance_url: &str, tag_name: &str) -> String {
|
||||||
|
format!("{}/tag/{}", instance_url, tag_name)
|
||||||
|
}
|
||||||
|
|
|
@ -72,7 +72,11 @@ async fn create_status(
|
||||||
post_data.mentions = mention_map.values()
|
post_data.mentions = mention_map.values()
|
||||||
.map(|profile| profile.id).collect();
|
.map(|profile| profile.id).collect();
|
||||||
post_data.tags = find_tags(&post_data.content);
|
post_data.tags = find_tags(&post_data.content);
|
||||||
post_data.content = replace_tags(&post_data.content, &post_data.tags);
|
post_data.content = replace_tags(
|
||||||
|
&instance.url(),
|
||||||
|
&post_data.content,
|
||||||
|
&post_data.tags,
|
||||||
|
);
|
||||||
let post = create_post(db_client, ¤t_user.id, post_data).await?;
|
let post = create_post(db_client, ¤t_user.id, post_data).await?;
|
||||||
// Federate
|
// Federate
|
||||||
let maybe_in_reply_to = match post.in_reply_to_id {
|
let maybe_in_reply_to = match post.in_reply_to_id {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
|
|
||||||
use crate::errors::ConversionError;
|
use crate::errors::ConversionError;
|
||||||
|
use crate::frontend::get_tag_page_url;
|
||||||
|
|
||||||
const HASHTAG_RE: &str = r"(?m)(?P<before>^|\s)#(?P<tag>\S+)";
|
const HASHTAG_RE: &str = r"(?m)(?P<before>^|\s)#(?P<tag>\S+)";
|
||||||
const HASHTAG_SECONDARY_RE: &str = r"^(?P<tag>[0-9A-Za-z]+)(?P<after>(\.|<br>|\.<br>)?)$";
|
const HASHTAG_SECONDARY_RE: &str = r"^(?P<tag>[0-9A-Za-z]+)(?P<after>(\.|<br>|\.<br>)?)$";
|
||||||
|
@ -23,7 +24,7 @@ pub fn find_tags(text: &str) -> Vec<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces hashtags with links
|
/// Replaces hashtags with links
|
||||||
pub fn replace_tags(text: &str, tags: &[String]) -> String {
|
pub fn replace_tags(instance_url: &str, text: &str, tags: &[String]) -> String {
|
||||||
let hashtag_re = Regex::new(HASHTAG_RE).unwrap();
|
let hashtag_re = Regex::new(HASHTAG_RE).unwrap();
|
||||||
let hashtag_secondary_re = Regex::new(HASHTAG_SECONDARY_RE).unwrap();
|
let hashtag_secondary_re = Regex::new(HASHTAG_SECONDARY_RE).unwrap();
|
||||||
let result = hashtag_re.replace_all(text, |caps: &Captures| {
|
let result = hashtag_re.replace_all(text, |caps: &Captures| {
|
||||||
|
@ -33,10 +34,11 @@ pub fn replace_tags(text: &str, tags: &[String]) -> String {
|
||||||
let tag_name = tag.to_lowercase();
|
let tag_name = tag.to_lowercase();
|
||||||
let after = secondary_caps["after"].to_string();
|
let after = secondary_caps["after"].to_string();
|
||||||
if tags.contains(&tag_name) {
|
if tags.contains(&tag_name) {
|
||||||
|
let tag_page_url = get_tag_page_url(instance_url, &tag_name);
|
||||||
format!(
|
format!(
|
||||||
r#"{}<a class="hashtag" href="/tag/{}">#{}</a>{}"#,
|
r#"{}<a class="hashtag" href="{}">#{}</a>{}"#,
|
||||||
before,
|
before,
|
||||||
tag_name,
|
tag_page_url,
|
||||||
tag,
|
tag,
|
||||||
after,
|
after,
|
||||||
)
|
)
|
||||||
|
@ -63,6 +65,8 @@ pub fn normalize_tag(tag: &str) -> Result<String, ConversionError> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
const INSTANCE_URL: &str = "https://example.com";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_find_tags() {
|
fn test_find_tags() {
|
||||||
let text = concat!(
|
let text = concat!(
|
||||||
|
@ -87,13 +91,13 @@ mod tests {
|
||||||
"more text #tag2",
|
"more text #tag2",
|
||||||
);
|
);
|
||||||
let tags = find_tags(text);
|
let tags = find_tags(text);
|
||||||
let output = replace_tags(&text, &tags);
|
let output = replace_tags(INSTANCE_URL, &text, &tags);
|
||||||
|
|
||||||
let expected_output = concat!(
|
let expected_output = concat!(
|
||||||
r#"@user1@server1 some text <a class="hashtag" href="/tag/testtag">#TestTag</a>."#, "\n",
|
r#"@user1@server1 some text <a class="hashtag" href="https://example.com/tag/testtag">#TestTag</a>."#, "\n",
|
||||||
r#"<a class="hashtag" href="/tag/tag1">#TAG1</a> <a class="hashtag" href="/tag/tag1">#tag1</a> "#,
|
r#"<a class="hashtag" href="https://example.com/tag/tag1">#TAG1</a> <a class="hashtag" href="https://example.com/tag/tag1">#tag1</a> "#,
|
||||||
r#"#test_underscore #test*special "#,
|
r#"#test_underscore #test*special "#,
|
||||||
r#"more text <a class="hashtag" href="/tag/tag2">#tag2</a>"#,
|
r#"more text <a class="hashtag" href="https://example.com/tag/tag2">#tag2</a>"#,
|
||||||
);
|
);
|
||||||
assert_eq!(output, expected_output);
|
assert_eq!(output, expected_output);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue