Save mentions

This commit is contained in:
Bat 2018-06-20 21:58:11 +01:00
parent c4cc4a4e13
commit d7b71848fc
5 changed files with 57 additions and 35 deletions

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive, decl_macro, iterator_find_map)]
#![feature(plugin, custom_derive, decl_macro, iterator_find_map, iterator_flatten)]
#![plugin(rocket_codegen)]
extern crate activitypub;

View File

@ -33,6 +33,7 @@ impl Mention {
get!(mentions);
find_by!(mentions, find_by_ap_url, ap_url as String);
list_by!(mentions, list_for_user, mentioned_id as i32);
list_by!(mentions, list_for_post, post_id as i32);
pub fn get_mentioned(&self, conn: &PgConnection) -> Option<User> {
User::get(conn, self.mentioned_id)
@ -46,6 +47,14 @@ impl Mention {
self.post_id.and_then(|id| Comment::get(conn, id))
}
pub fn build_activity(conn: &PgConnection, ment: String) -> link::Mention {
let user = User::find_by_fqn(conn, ment.clone());
let mut mention = link::Mention::default();
mention.link_props.set_href_string(user.clone().map(|u| u.ap_url).unwrap_or(String::new())).expect("Error setting mention's href");
mention.link_props.set_name_string(format!("@{}", ment)).expect("Error setting mention's name");
mention
}
pub fn to_activity(&self, conn: &PgConnection) -> link::Mention {
let user = self.get_mentioned(conn);
let mut mention = link::Mention::default();

View File

@ -145,6 +145,8 @@ impl Post {
let mut to = self.get_receivers_urls(conn);
to.push(PUBLIC_VISIBILTY.to_string());
let mentions = Mention::list_for_post(conn, self.id).into_iter().map(|m| m.to_activity(conn)).collect::<Vec<link::Mention>>();
let mut article = Article::default();
article.object_props = ObjectProperties {
name: Some(serde_json::to_value(self.title.clone()).unwrap()),
@ -152,11 +154,11 @@ impl Post {
attributed_to: Some(serde_json::to_value(self.get_authors(conn).into_iter().map(|x| x.ap_url).collect::<Vec<String>>()).unwrap()),
content: Some(serde_json::to_value(self.content.clone()).unwrap()),
published: Some(serde_json::to_value(self.creation_date).unwrap()),
tag: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()),
tag: Some(serde_json::to_value(mentions).unwrap()),
url: Some(serde_json::to_value(self.compute_id(conn)).unwrap()),
to: Some(serde_json::to_value(to).unwrap()),
cc: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()),
..ObjectProperties::default()
..ObjectProperties::default()
};
article
}

View File

@ -4,11 +4,12 @@ use rocket::response::{Redirect, Flash};
use rocket_contrib::Template;
use serde_json;
use activity_pub::{broadcast, context, activity_pub, ActivityPub};
use activity_pub::{broadcast, context, activity_pub, ActivityPub, Id};
use db_conn::DbConn;
use models::{
blogs::*,
comments::Comment,
mentions::Mention,
post_authors::*,
posts::*,
users::User
@ -87,7 +88,7 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() {
Redirect::to(uri!(new: blog = blog_name))
} else {
let content = utils::md_to_html(form.content.to_string().as_ref());
let (content, mentions) = utils::md_to_html(form.content.to_string().as_ref());
let post = Post::insert(&*conn, NewPost {
blog_id: blog.id,
@ -104,6 +105,10 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
author_id: user.id
});
for m in mentions.into_iter() {
Mention::from_activity(&*conn, Mention::build_activity(&*conn, m), Id::new(post.compute_id(&*conn)));
}
let act = post.create_activity(&*conn);
broadcast(&*conn, &user, act, user.get_followers(&*conn));

View File

@ -20,45 +20,51 @@ pub fn requires_login(message: &str, url: Uri) -> Flash<Redirect> {
Flash::new(Redirect::to(Uri::new(format!("/login?m={}", gettext(message.to_string())))), "callback", url.as_str())
}
pub fn md_to_html(md: &str) -> String {
/// Returns (HTML, mentions)
pub fn md_to_html(md: &str) -> (String, Vec<String>) {
let parser = Parser::new_ext(md, Options::all());
let parser = parser.flat_map(|evt| match evt {
Event::Text(txt) => txt.chars().fold((vec![], false, String::new(), 0), |(mut events, in_mention, text_acc, n), c| {
if in_mention {
if (c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_') && (n < (txt.chars().count() - 1)) {
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1)
} else {
let mention = text_acc + c.to_string().as_ref();
let short_mention = mention.clone();
let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or("");
let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into());
let (parser, mentions): (Vec<Vec<Event>>, Vec<Vec<String>>) = parser.map(|evt| match evt {
Event::Text(txt) => {
let (evts, _, _, _, new_mentions) = txt.chars().fold((vec![], false, String::new(), 0, vec![]), |(mut events, in_mention, text_acc, n, mut mentions), c| {
if in_mention {
if (c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_') && (n < (txt.chars().count() - 1)) {
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions)
} else {
let mention = text_acc + c.to_string().as_ref();
let short_mention = mention.clone();
let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or("");
let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into());
events.push(Event::Start(link.clone()));
events.push(Event::Text(format!("@{}", short_mention).into()));
events.push(Event::End(link));
mentions.push(mention);
events.push(Event::Start(link.clone()));
events.push(Event::Text(format!("@{}", short_mention).into()));
events.push(Event::End(link));
(events, false, c.to_string(), n + 1)
}
} else {
if c == '@' {
events.push(Event::Text(text_acc.into()));
(events, true, String::new(), n + 1)
} else {
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
(events, false, c.to_string(), n + 1, mentions)
}
} else {
if c == '@' {
events.push(Event::Text(text_acc.into()));
(events, true, String::new(), n + 1, mentions)
} else {
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
}
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions)
}
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1)
}
}
}).0,
_ => vec![evt]
});
});
(evts, new_mentions)
},
_ => (vec![evt], vec![])
}).unzip();
let parser = parser.into_iter().flatten();
let mentions = mentions.into_iter().flatten();
// TODO: fetch mentionned profiles in background, if needed
let mut buf = String::new();
html::push_html(&mut buf, parser);
buf
(buf, mentions.collect())
}