Link hashtags in posts to corresponding tag timeline pages

This commit is contained in:
silverpill 2021-12-12 19:11:04 +00:00
parent da918d2296
commit 816cae6b78
5 changed files with 27 additions and 13 deletions

View file

@ -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);
}; };

View file

@ -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();

View file

@ -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)
}

View file

@ -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, &current_user.id, post_data).await?; let post = create_post(db_client, &current_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 {

View file

@ -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);
} }