My personal Kibou development repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

renderer.rs 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. use activity;
  2. use actor;
  3. use chrono::prelude::*;
  4. use database;
  5. use env;
  6. use mastodon_api::{RegistrationForm, Status, StatusForm};
  7. use raito_fe::{self, Authentication, LocalConfiguration, LoginForm};
  8. use rocket::http::{Cookie, Cookies};
  9. use rocket::request::LenientForm;
  10. use rocket::response::Redirect;
  11. use rocket::Rocket;
  12. use rocket_contrib::templates::Template;
  13. use std::collections::HashMap;
  14. use std::fs;
  15. pub fn about(configuration: LocalConfiguration, authentication: Authentication) -> Template {
  16. let mut context = HashMap::<String, String>::new();
  17. context.extend(configuration);
  18. context.extend(prepare_authentication_context(&authentication));
  19. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  20. context.insert(
  21. "content".to_string(),
  22. fs::read_to_string("static/raito_fe/html/about.html").unwrap_or_else(|_| {
  23. String::from(
  24. "<h2>About this node</h2>
  25. This is a placeholder text, it can be edited in \"static/raito_fe/html/about.html\"
  26. ",
  27. )
  28. }),
  29. );
  30. return Template::render("raito_fe/about", context);
  31. }
  32. pub fn account_by_local_id(
  33. configuration: LocalConfiguration,
  34. authentication: Authentication,
  35. id: String,
  36. ) -> Template {
  37. let mut context = HashMap::<String, String>::new();
  38. context.extend(configuration.clone());
  39. context.extend(prepare_authentication_context(&authentication));
  40. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  41. match raito_fe::api_controller::get_account(&id) {
  42. Ok(account) => {
  43. match &authentication.account {
  44. Some(account) => {
  45. if &account.id != &id {
  46. match &authentication.token {
  47. Some(token) => {
  48. match raito_fe::api_controller::relationships_by_token(
  49. &token,
  50. vec![id.parse::<i64>().unwrap()],
  51. ) {
  52. Some(relationship) => {
  53. context.insert(
  54. String::from("account_relationship_following"),
  55. relationship[0].following.to_string(),
  56. );
  57. }
  58. None => {
  59. context.insert(
  60. String::from("account_relationship_following"),
  61. String::from(""),
  62. );
  63. }
  64. }
  65. }
  66. None => {
  67. context.insert(
  68. String::from("account_relationship_following"),
  69. String::from(""),
  70. );
  71. }
  72. }
  73. } else {
  74. context.insert(
  75. String::from("account_relationship_following"),
  76. String::from(""),
  77. );
  78. }
  79. }
  80. None => {
  81. context.insert(
  82. String::from("account_relationship_following"),
  83. String::from(""),
  84. );
  85. }
  86. }
  87. context.insert(String::from("account_acct"), account.acct);
  88. context.insert(String::from("account_display_name"), account.display_name);
  89. context.insert(String::from("account_avatar"), account.avatar);
  90. context.insert(
  91. String::from("account_followers_count"),
  92. account.followers_count.to_string(),
  93. );
  94. context.insert(
  95. String::from("account_following_count"),
  96. account.following_count.to_string(),
  97. );
  98. context.insert(String::from("account_header"), account.header);
  99. context.insert(String::from("account_id"), account.id.clone());
  100. context.insert(String::from("account_note"), account.note);
  101. context.insert(
  102. String::from("account_statuses_count"),
  103. account.statuses_count.to_string(),
  104. );
  105. context.insert(
  106. String::from("account_timeline"),
  107. user_timeline(configuration, authentication, account.id),
  108. );
  109. return Template::render("raito_fe/account", context);
  110. }
  111. Err(_) => Template::render("raito_fe/index", context),
  112. }
  113. }
  114. pub fn account_by_username(
  115. configuration: LocalConfiguration,
  116. authentication: Authentication,
  117. username: String,
  118. ) -> Template {
  119. let database = database::establish_connection();
  120. let mut context = HashMap::<String, String>::new();
  121. context.extend(configuration.clone());
  122. context.extend(prepare_authentication_context(&authentication));
  123. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  124. match actor::get_local_actor_by_preferred_username(&database, &username) {
  125. Ok(actor) => account_by_local_id(configuration, authentication, actor.id.to_string()),
  126. Err(_) => Template::render("raito_fe/index", context),
  127. }
  128. }
  129. pub fn account_follow(
  130. configuration: LocalConfiguration,
  131. authentication: Authentication,
  132. id: i64,
  133. unfollow: bool,
  134. ) -> Template {
  135. let database = database::establish_connection();
  136. let mut context = HashMap::<String, String>::new();
  137. context.extend(configuration.clone());
  138. context.extend(prepare_authentication_context(&authentication));
  139. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  140. match &authentication.token {
  141. Some(token) => {
  142. if unfollow {
  143. raito_fe::api_controller::unfollow(token, id);
  144. } else {
  145. raito_fe::api_controller::follow(token, id);
  146. }
  147. return account_by_local_id(configuration, authentication, id.to_string());
  148. }
  149. None => return account_by_local_id(configuration, authentication, id.to_string()),
  150. }
  151. }
  152. pub fn compose(
  153. configuration: LocalConfiguration,
  154. authentication: Authentication,
  155. in_reply_to: Option<i64>,
  156. ) -> Template {
  157. let mut context = HashMap::<String, String>::new();
  158. context.extend(configuration.clone());
  159. context.extend(prepare_authentication_context(&authentication));
  160. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  161. if authentication.account.is_none() {
  162. return Template::render("raito_fe/infoscreen", context);
  163. } else {
  164. match in_reply_to {
  165. Some(head_status_id) => {
  166. let renderer = rocket::ignite().attach(Template::fairing());
  167. match raito_fe::api_controller::get_status(head_status_id.to_string()) {
  168. Ok(status) => context.insert(
  169. String::from("head_status"),
  170. format!(
  171. "<input type=\"hidden\" name=\"in_reply_to_id\" value=\"{}\">{}",
  172. status.id.clone(),
  173. raw_status(configuration, &authentication, status, &renderer)
  174. ),
  175. ),
  176. Err(_) => context.insert(String::from("head_status"), String::from("")),
  177. };
  178. }
  179. None => {
  180. context.insert(String::from("head_status"), String::from(""));
  181. }
  182. }
  183. return Template::render("raito_fe/status_post", context);
  184. }
  185. }
  186. pub fn compose_post(
  187. configuration: LocalConfiguration,
  188. authentication: Authentication,
  189. form: LenientForm<StatusForm>,
  190. ) -> Redirect {
  191. let mut context = HashMap::<String, String>::new();
  192. context.extend(configuration.clone());
  193. context.extend(prepare_authentication_context(&authentication));
  194. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  195. match &authentication.token {
  196. Some(token) => {
  197. raito_fe::api_controller::post_status(form, &token);
  198. return Redirect::to("/timeline/home");
  199. }
  200. None => return Redirect::to("/"),
  201. }
  202. }
  203. pub fn conversation(
  204. configuration: LocalConfiguration,
  205. authentication: Authentication,
  206. id: String,
  207. ) -> Template {
  208. let mut context = HashMap::<String, String>::new();
  209. let rocket_renderer = rocket::ignite().attach(Template::fairing());
  210. context.extend(configuration.clone());
  211. context.extend(prepare_authentication_context(&authentication));
  212. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  213. match raito_fe::api_controller::get_status(id.clone()) {
  214. Ok(status) => {
  215. let mut renderered_statuses: Vec<String> = vec![];
  216. let mut parent_statuses: Vec<Status> = vec![];
  217. let mut child_statuses: Vec<Status> = vec![];
  218. let mut timeline_parameters = HashMap::<String, String>::new();
  219. match raito_fe::api_controller::get_status_context(id) {
  220. Ok(context) => {
  221. parent_statuses =
  222. serde_json::from_value(context["ancestors"].to_owned()).unwrap();
  223. child_statuses =
  224. serde_json::from_value(context["descendants"].to_owned()).unwrap();
  225. }
  226. Err(()) => (),
  227. }
  228. for parent in parent_statuses {
  229. renderered_statuses.push(raw_status(
  230. configuration.clone(),
  231. &authentication,
  232. parent,
  233. &rocket_renderer,
  234. ));
  235. }
  236. renderered_statuses.push(raw_status(
  237. configuration.clone(),
  238. &authentication,
  239. status,
  240. &rocket_renderer,
  241. ));
  242. for child in child_statuses {
  243. renderered_statuses.push(raw_status(
  244. configuration.clone(),
  245. &authentication,
  246. child,
  247. &rocket_renderer,
  248. ));
  249. }
  250. context.insert(String::from("timeline_name"), String::from("Conversation"));
  251. timeline_parameters.insert(String::from("statuses"), renderered_statuses.join(""));
  252. timeline_parameters.extend(configuration.clone());
  253. timeline_parameters.extend(prepare_authentication_context(&authentication));
  254. context.insert(
  255. String::from("timeline"),
  256. Template::show(
  257. &rocket_renderer,
  258. "raito_fe/components/timeline",
  259. timeline_parameters,
  260. )
  261. .unwrap(),
  262. );
  263. return Template::render("raito_fe/timeline_view", context);
  264. }
  265. Err(_) => Template::render("raito_fe/index", context),
  266. }
  267. }
  268. // Note: This case only occurs if the Raito-FE is set as the main UI of Kibou
  269. pub fn conversation_by_uri(
  270. configuration: LocalConfiguration,
  271. authentication: Authentication,
  272. id: String,
  273. ) -> Template {
  274. let database = database::establish_connection();
  275. let mut context = HashMap::<String, String>::new();
  276. let object_id = format!(
  277. "{}://{}/objects/{}",
  278. env::get_value(String::from("endpoint.base_scheme")),
  279. env::get_value(String::from("endpoint.base_domain")),
  280. id
  281. );
  282. context.extend(configuration.clone());
  283. context.extend(prepare_authentication_context(&authentication));
  284. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  285. match activity::get_ap_object_by_id(&database, &object_id) {
  286. Ok(activity) => conversation(configuration, authentication, activity.id.to_string()),
  287. Err(_) => Template::render("raito_fe/index", context),
  288. }
  289. }
  290. pub fn home_timeline(
  291. configuration: LocalConfiguration,
  292. authentication: Authentication,
  293. ) -> Template {
  294. let mut context = HashMap::<String, String>::new();
  295. let rocket_renderer = rocket::ignite().attach(Template::fairing());
  296. let mut timeline_parameters = HashMap::<String, String>::new();
  297. context.extend(configuration.clone());
  298. context.extend(prepare_authentication_context(&authentication));
  299. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  300. match &authentication.token {
  301. Some(token) => {
  302. if configuration.0.get("javascript_enabled").unwrap() == "true" {
  303. context.insert(
  304. String::from("timeline"),
  305. Template::show(
  306. &rocket_renderer,
  307. "raito_fe/components/timeline",
  308. timeline_parameters,
  309. )
  310. .unwrap(),
  311. );
  312. return Template::render("raito_fe/timeline_view", context);
  313. } else {
  314. match raito_fe::api_controller::home_timeline(&format!("Bearer: {}", token)) {
  315. Ok(statuses) => {
  316. let mut renderered_statuses: Vec<String> = vec![];
  317. for status in statuses {
  318. renderered_statuses.push(raw_status(
  319. configuration.clone(),
  320. &authentication,
  321. status,
  322. &rocket_renderer,
  323. ));
  324. }
  325. context
  326. .insert(String::from("timeline_name"), String::from("Home Timeline"));
  327. timeline_parameters.extend(configuration.clone());
  328. timeline_parameters
  329. .insert(String::from("statuses"), renderered_statuses.join(""));
  330. context.insert(
  331. String::from("timeline"),
  332. Template::show(
  333. &rocket_renderer,
  334. "raito_fe/components/timeline",
  335. timeline_parameters,
  336. )
  337. .unwrap(),
  338. );
  339. return Template::render("raito_fe/timeline_view", context);
  340. }
  341. Err(_) => Template::render("raito_fe/index", context),
  342. }
  343. }
  344. }
  345. None => return public_timeline(configuration, authentication, false),
  346. }
  347. }
  348. pub fn index(configuration: LocalConfiguration, authentication: Authentication) -> Template {
  349. let mut context = HashMap::<String, String>::new();
  350. match &authentication.account {
  351. Some(account) => return home_timeline(configuration, authentication),
  352. None => {
  353. context.extend(configuration);
  354. context.extend(prepare_authentication_context(&authentication));
  355. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  356. return Template::render("raito_fe/infoscreen", context);
  357. }
  358. }
  359. }
  360. pub fn login(configuration: LocalConfiguration, authentication: Authentication) -> Template {
  361. let mut context = HashMap::<String, String>::new();
  362. context.extend(configuration.clone());
  363. context.extend(prepare_authentication_context(&authentication));
  364. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  365. if authentication.account.is_none() {
  366. return Template::render("raito_fe/login", context);
  367. } else {
  368. return public_timeline(configuration, authentication, false);
  369. }
  370. }
  371. pub fn login_post(
  372. configuration: LocalConfiguration,
  373. authentication: Authentication,
  374. mut cookies: Cookies,
  375. form: LenientForm<LoginForm>,
  376. ) -> Result<Redirect, Template> {
  377. let mut context = HashMap::<String, String>::new();
  378. context.extend(configuration.clone());
  379. context.extend(prepare_authentication_context(&authentication));
  380. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  381. if authentication.account.is_none() {
  382. match raito_fe::api_controller::login(form) {
  383. Some(token) => {
  384. cookies.add_private(Cookie::new("oauth_token", token));
  385. return Ok(Redirect::to("/timeline/home"));
  386. }
  387. None => Err(Template::render("raito_fe/login", context)),
  388. }
  389. } else {
  390. return Ok(Redirect::to("/timeline/home"));
  391. }
  392. }
  393. pub fn public_timeline(
  394. configuration: LocalConfiguration,
  395. authentication: Authentication,
  396. local: bool,
  397. ) -> Template {
  398. let rocket_renderer = rocket::ignite().attach(Template::fairing());
  399. let mut context = HashMap::<String, String>::new();
  400. let mut timeline_parameters = HashMap::<String, String>::new();
  401. context.extend(configuration.clone());
  402. context.extend(prepare_authentication_context(&authentication));
  403. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  404. if local {
  405. context.insert(
  406. String::from("timeline_name"),
  407. String::from("Public Timeline"),
  408. );
  409. } else {
  410. context.insert(
  411. String::from("timeline_name"),
  412. String::from("Global Timeline"),
  413. );
  414. }
  415. timeline_parameters.extend(configuration.clone());
  416. if configuration.0.get("javascript_enabled").unwrap() == "true" {
  417. context.insert(
  418. String::from("timeline"),
  419. Template::show(
  420. &rocket_renderer,
  421. "raito_fe/components/timeline",
  422. timeline_parameters,
  423. )
  424. .unwrap(),
  425. );
  426. return Template::render("raito_fe/timeline_view", context);
  427. } else {
  428. match raito_fe::api_controller::get_public_timeline(local) {
  429. Ok(statuses) => {
  430. let mut renderered_statuses: Vec<String> = vec![];
  431. for status in statuses {
  432. renderered_statuses.push(raw_status(
  433. configuration.clone(),
  434. &authentication,
  435. status,
  436. &rocket_renderer,
  437. ));
  438. }
  439. timeline_parameters.insert(String::from("statuses"), renderered_statuses.join(""));
  440. context.insert(
  441. String::from("timeline"),
  442. Template::show(
  443. &rocket_renderer,
  444. "raito_fe/components/timeline",
  445. timeline_parameters,
  446. )
  447. .unwrap(),
  448. );
  449. return Template::render("raito_fe/timeline_view", context);
  450. }
  451. Err(_) => Template::render("raito_fe/index", context),
  452. }
  453. }
  454. }
  455. pub fn raw_status(
  456. configuration: LocalConfiguration,
  457. authentication: &Authentication,
  458. status: Status,
  459. rocket: &Rocket,
  460. ) -> String {
  461. let mut context = prepare_status_context(status);
  462. context.extend(configuration);
  463. context.extend(prepare_authentication_context(authentication));
  464. return Template::show(rocket, "raito_fe/components/status", context).unwrap();
  465. }
  466. pub fn register_post(
  467. configuration: LocalConfiguration,
  468. authentication: Authentication,
  469. mut cookies: Cookies,
  470. form: LenientForm<RegistrationForm>,
  471. ) -> Result<Redirect, Template> {
  472. let mut context = HashMap::<String, String>::new();
  473. context.extend(configuration.clone());
  474. context.extend(prepare_authentication_context(&authentication));
  475. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  476. if authentication.account.is_none() {
  477. match raito_fe::api_controller::register(form) {
  478. Some(token) => {
  479. cookies.add_private(Cookie::new("oauth_token", token));
  480. return Ok(Redirect::to("/timeline/home"));
  481. }
  482. None => return Err(Template::render("raito_fe/infoscreen", context)),
  483. }
  484. } else {
  485. return Ok(Redirect::to("/timeline/home"));
  486. }
  487. }
  488. pub fn settings(configuration: LocalConfiguration, authentication: Authentication) -> Template {
  489. let mut context = HashMap::<String, String>::new();
  490. context.extend(configuration);
  491. context.extend(prepare_authentication_context(&authentication));
  492. context.insert("stylesheet".to_string(), raito_fe::get_stylesheet());
  493. return Template::render("raito_fe/settings", context);
  494. }
  495. pub fn user_timeline(
  496. configuration: LocalConfiguration,
  497. authentication: Authentication,
  498. id: String,
  499. ) -> String {
  500. let mut context = HashMap::<String, String>::new();
  501. match raito_fe::api_controller::get_user_timeline(id) {
  502. Ok(statuses) => {
  503. let mut renderered_statuses: Vec<String> = vec![];
  504. let rocket_renderer = rocket::ignite().attach(Template::fairing());
  505. for status in statuses {
  506. renderered_statuses.push(raw_status(
  507. configuration.clone(),
  508. &authentication,
  509. status,
  510. &rocket_renderer,
  511. ));
  512. }
  513. context.extend(configuration);
  514. context.extend(prepare_authentication_context(&authentication));
  515. context.insert(String::from("statuses"), renderered_statuses.join(""));
  516. context.insert(String::from("timeline_name"), String::from("User Timeline"));
  517. return Template::show(&rocket_renderer, "raito_fe/components/timeline", context)
  518. .unwrap();
  519. }
  520. Err(_) => String::from(""),
  521. }
  522. }
  523. fn prepare_authentication_context(authentication: &Authentication) -> HashMap<String, String> {
  524. let mut context = HashMap::<String, String>::new();
  525. match &authentication.account {
  526. Some(account) => {
  527. context.insert(String::from("authenticated_account"), true.to_string());
  528. context.insert(
  529. String::from("authenticated_account_display_name"),
  530. (*account.display_name).to_string(),
  531. );
  532. context.insert(
  533. String::from("authenticated_account_avatar"),
  534. (*account.avatar).to_string(),
  535. );
  536. context.insert(
  537. String::from("authenticated_account_id"),
  538. (*account.id).to_string(),
  539. );
  540. }
  541. None => {
  542. context.insert(String::from("authenticated_account"), false.to_string());
  543. context.insert(
  544. String::from("authenticated_account_display_name"),
  545. String::from("Guest"),
  546. );
  547. context.insert(
  548. String::from("authenticated_account_avatar"),
  549. String::from("/static/assets/default_avatar.png"),
  550. );
  551. context.insert(String::from("authenticated_account_id"), String::from(""));
  552. }
  553. }
  554. return context;
  555. }
  556. fn prepare_status_context(status: Status) -> HashMap<String, String> {
  557. let mut context = HashMap::<String, String>::new();
  558. let date: String;
  559. let favourites_count: String;
  560. let replies_count: String;
  561. let shares_count: String;
  562. match DateTime::parse_from_rfc3339(&status.created_at) {
  563. Ok(parsed_date) => date = parsed_date.format("%B %d, %Y, %H:%M:%S").to_string(),
  564. Err(_) => date = String::from("Unknown date"),
  565. }
  566. if status.favourites_count > 0 {
  567. favourites_count = status.favourites_count.to_string()
  568. } else {
  569. favourites_count = String::from("")
  570. }
  571. if status.replies_count > 0 {
  572. replies_count = status.replies_count.to_string()
  573. } else {
  574. replies_count = String::from("")
  575. }
  576. if status.reblogs_count > 0 {
  577. shares_count = status.reblogs_count.to_string()
  578. } else {
  579. shares_count = String::from("")
  580. }
  581. context.insert(String::from("status_account_acct"), status.account.acct);
  582. context.insert(String::from("status_account_avatar"), status.account.avatar);
  583. context.insert(
  584. String::from("status_account_displayname"),
  585. status.account.display_name,
  586. );
  587. context.insert(
  588. String::from("status_account_url"),
  589. format!("/account/{}", status.account.id),
  590. );
  591. context.insert(String::from("status_content"), status.content);
  592. context.insert(String::from("status_created_at"), date);
  593. context.insert(String::from("status_favourites_count"), favourites_count);
  594. context.insert(String::from("status_id"), status.id.to_string());
  595. context.insert(
  596. String::from("status_reblog"),
  597. status.reblog.is_some().to_string(),
  598. );
  599. let mut media_context: Vec<String> = Vec::new();
  600. for attachment in status.media_attachments {
  601. media_context.push(format!("<img src=\"{}\">", attachment.url));
  602. }
  603. context.insert(
  604. String::from("status_media_attachments"),
  605. media_context.join(""),
  606. );
  607. match status.reblog {
  608. Some(reblog_status) => {
  609. let reblog: Status = serde_json::from_value(reblog_status).unwrap();
  610. context.insert(String::from("reblog_account_acct"), reblog.account.acct);
  611. context.insert(String::from("reblog_account_avatar"), reblog.account.avatar);
  612. context.insert(
  613. String::from("reblog_account_url"),
  614. format!("/account/{}", reblog.id),
  615. );
  616. context.insert(String::from("reblog_content"), reblog.content);
  617. }
  618. None => {
  619. context.insert(String::from("reblog_account_acct"), String::from(""));
  620. context.insert(String::from("reblog_account_avatar"), String::from(""));
  621. context.insert(String::from("reblog_account_url"), String::from(""));
  622. context.insert(String::from("reblog_content"), String::from(""));
  623. }
  624. }
  625. context.insert(String::from("status_reblogs_count"), shares_count);
  626. context.insert(String::from("status_replies_count"), replies_count);
  627. context.insert(String::from("status_uri"), status.uri);
  628. context.insert(String::from("status_url"), format!("/status/{}", status.id));
  629. return context;
  630. }