Remove useless pagination routes (#351)

Rocket 0.4 let us have routes with optional query parameter
This commit is contained in:
Baptiste Gelez 2018-12-13 22:20:19 +01:00 committed by GitHub
parent e139008d35
commit b0089e59b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 105 additions and 155 deletions

View file

@ -79,7 +79,6 @@ fn main() {
rocket::ignite() rocket::ignite()
.mount("/", routes![ .mount("/", routes![
routes::blogs::paginated_details,
routes::blogs::details, routes::blogs::details,
routes::blogs::activity_details, routes::blogs::activity_details,
routes::blogs::outbox, routes::blogs::outbox,
@ -93,17 +92,12 @@ fn main() {
routes::comments::activity_pub, routes::comments::activity_pub,
routes::instance::index, routes::instance::index,
routes::instance::paginated_local,
routes::instance::local, routes::instance::local,
routes::instance::paginated_feed,
routes::instance::feed, routes::instance::feed,
routes::instance::paginated_federated,
routes::instance::federated, routes::instance::federated,
routes::instance::admin, routes::instance::admin,
routes::instance::admin_instances, routes::instance::admin_instances,
routes::instance::admin_instances_paginated,
routes::instance::admin_users, routes::instance::admin_users,
routes::instance::admin_users_paginated,
routes::instance::ban, routes::instance::ban,
routes::instance::toggle_block, routes::instance::toggle_block,
routes::instance::update_settings, routes::instance::update_settings,
@ -122,12 +116,10 @@ fn main() {
routes::medias::delete, routes::medias::delete,
routes::medias::set_avatar, routes::medias::set_avatar,
routes::notifications::paginated_notifications,
routes::notifications::notifications, routes::notifications::notifications,
routes::notifications::notifications_auth, routes::notifications::notifications_auth,
routes::posts::details, routes::posts::details,
routes::posts::details_response,
routes::posts::activity_details, routes::posts::activity_details,
routes::posts::edit, routes::posts::edit,
routes::posts::update, routes::posts::update,
@ -142,20 +134,17 @@ fn main() {
routes::search::search, routes::search::search,
routes::session::new, routes::session::new,
routes::session::new_message,
routes::session::create, routes::session::create,
routes::session::delete, routes::session::delete,
routes::static_files, routes::static_files,
routes::tags::tag, routes::tags::tag,
routes::tags::paginated_tag,
routes::user::me, routes::user::me,
routes::user::details, routes::user::details,
routes::user::dashboard, routes::user::dashboard,
routes::user::dashboard_auth, routes::user::dashboard_auth,
routes::user::followers_paginated,
routes::user::followers, routes::user::followers,
routes::user::edit, routes::user::edit,
routes::user::edit_auth, routes::user::edit_auth,

View file

@ -24,7 +24,8 @@ use template_utils::Ructe;
use Searcher; use Searcher;
#[get("/~/<name>?<page>", rank = 2)] #[get("/~/<name>?<page>", rank = 2)]
pub fn paginated_details(intl: I18n, name: String, conn: DbConn, user: Option<User>, page: Page) -> Result<Ructe, Ructe> { pub fn details(intl: I18n, name: String, conn: DbConn, user: Option<User>, page: Option<Page>) -> Result<Ructe, Ructe> {
let page = page.unwrap_or_default();
let blog = Blog::find_by_fqn(&*conn, &name) let blog = Blog::find_by_fqn(&*conn, &name)
.ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?; .ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?;
let posts = Post::blog_page(&*conn, &blog, page.limits()); let posts = Post::blog_page(&*conn, &blog, page.limits());
@ -44,11 +45,6 @@ pub fn paginated_details(intl: I18n, name: String, conn: DbConn, user: Option<Us
))) )))
} }
#[get("/~/<name>", rank = 3)]
pub fn details(intl: I18n, name: String, conn: DbConn, user: Option<User>) -> Result<Ructe, Ructe> {
paginated_details(intl, name, conn, user, Page::first())
}
#[get("/~/<name>", rank = 1)] #[get("/~/<name>", rank = 1)]
pub fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> Option<ActivityStream<CustomGroup>> { pub fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> Option<ActivityStream<CustomGroup>> {
let blog = Blog::find_local(&*conn, &name)?; let blog = Blog::find_local(&*conn, &name)?;
@ -118,7 +114,7 @@ pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I1
is_owner: true is_owner: true
}); });
Ok(Redirect::to(uri!(details: name = slug.clone()))) Ok(Redirect::to(uri!(details: name = slug.clone(), page = _)))
} else { } else {
Err(render!(blogs::new( Err(render!(blogs::new(
&(&*conn, &intl.catalog, Some(user)), &(&*conn, &intl.catalog, Some(user)),

View file

@ -57,7 +57,7 @@ pub fn create(blog_name: String, slug: String, form: LenientForm<NewCommentForm>
let user_clone = user.clone(); let user_clone = user.clone();
worker.execute(move || broadcast(&user_clone, new_comment, dest)); worker.execute(move || broadcast(&user_clone, new_comment, dest));
Redirect::to(uri!(super::posts::details: blog = blog_name, slug = slug)) Redirect::to(uri!(super::posts::details: blog = blog_name, slug = slug, responding_to = _))
}) })
.map_err(|errors| { .map_err(|errors| {
// TODO: de-duplicate this code // TODO: de-duplicate this code

View file

@ -25,13 +25,13 @@ use Searcher;
pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Ructe { pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Ructe {
match Instance::get_local(&*conn) { match Instance::get_local(&*conn) {
Some(inst) => { Some(inst) => {
let federated = Post::get_recents_page(&*conn, Page::first().limits()); let federated = Post::get_recents_page(&*conn, Page::default().limits());
let local = Post::get_instance_page(&*conn, inst.id, Page::first().limits()); let local = Post::get_instance_page(&*conn, inst.id, Page::default().limits());
let user_feed = user.clone().map(|user| { let user_feed = user.clone().map(|user| {
let followed = user.get_following(&*conn); let followed = user.get_following(&*conn);
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>(); let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
in_feed.push(user.id); in_feed.push(user.id);
Post::user_feed_page(&*conn, in_feed, Page::first().limits()) Post::user_feed_page(&*conn, in_feed, Page::default().limits())
}); });
render!(instance::index( render!(instance::index(
@ -53,7 +53,8 @@ pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Ructe {
} }
#[get("/local?<page>")] #[get("/local?<page>")]
pub fn paginated_local(conn: DbConn, user: Option<User>, page: Page, intl: I18n) -> Ructe { pub fn local(conn: DbConn, user: Option<User>, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
let instance = Instance::get_local(&*conn).expect("instance::paginated_local: local instance not found error"); let instance = Instance::get_local(&*conn).expect("instance::paginated_local: local instance not found error");
let articles = Post::get_instance_page(&*conn, instance.id, page.limits()); let articles = Post::get_instance_page(&*conn, instance.id, page.limits());
render!(instance::local( render!(instance::local(
@ -65,18 +66,9 @@ pub fn paginated_local(conn: DbConn, user: Option<User>, page: Page, intl: I18n)
)) ))
} }
#[get("/local")]
pub fn local(conn: DbConn, user: Option<User>, intl: I18n) -> Ructe {
paginated_local(conn, user, Page::first(), intl)
}
#[get("/feed")]
pub fn feed(conn: DbConn, user: User, intl: I18n) -> Ructe {
paginated_feed(conn, user, Page::first(), intl)
}
#[get("/feed?<page>")] #[get("/feed?<page>")]
pub fn paginated_feed(conn: DbConn, user: User, page: Page, intl: I18n) -> Ructe { pub fn feed(conn: DbConn, user: User, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
let followed = user.get_following(&*conn); let followed = user.get_following(&*conn);
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>(); let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
in_feed.push(user.id); in_feed.push(user.id);
@ -89,13 +81,9 @@ pub fn paginated_feed(conn: DbConn, user: User, page: Page, intl: I18n) -> Ructe
)) ))
} }
#[get("/federated")]
pub fn federated(conn: DbConn, user: Option<User>, intl: I18n) -> Ructe {
paginated_federated(conn, user, Page::first(), intl)
}
#[get("/federated?<page>")] #[get("/federated?<page>")]
pub fn paginated_federated(conn: DbConn, user: Option<User>, page: Page, intl: I18n) -> Ructe { pub fn federated(conn: DbConn, user: Option<User>, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
let articles = Post::get_recents_page(&*conn, page.limits()); let articles = Post::get_recents_page(&*conn, page.limits());
render!(instance::federated( render!(instance::federated(
&(&*conn, &intl.catalog, user), &(&*conn, &intl.catalog, user),
@ -156,13 +144,9 @@ pub fn update_settings(conn: DbConn, admin: Admin, form: LenientForm<InstanceSet
}) })
} }
#[get("/admin/instances")]
pub fn admin_instances(admin: Admin, conn: DbConn, intl: I18n) -> Ructe {
admin_instances_paginated(admin, conn, Page::first(), intl)
}
#[get("/admin/instances?<page>")] #[get("/admin/instances?<page>")]
pub fn admin_instances_paginated(admin: Admin, conn: DbConn, page: Page, intl: I18n) -> Ructe { pub fn admin_instances(admin: Admin, conn: DbConn, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
let instances = Instance::page(&*conn, page.limits()); let instances = Instance::page(&*conn, page.limits());
render!(instance::list( render!(instance::list(
&(&*conn, &intl.catalog, Some(admin.0)), &(&*conn, &intl.catalog, Some(admin.0)),
@ -179,16 +163,12 @@ pub fn toggle_block(_admin: Admin, conn: DbConn, id: i32) -> Redirect {
inst.toggle_block(&*conn); inst.toggle_block(&*conn);
} }
Redirect::to(uri!(admin_instances)) Redirect::to(uri!(admin_instances: page = _))
}
#[get("/admin/users")]
pub fn admin_users(admin: Admin, conn: DbConn, intl: I18n) -> Ructe {
admin_users_paginated(admin, conn, Page::first(), intl)
} }
#[get("/admin/users?<page>")] #[get("/admin/users?<page>")]
pub fn admin_users_paginated(admin: Admin, conn: DbConn, page: Page, intl: I18n) -> Ructe { pub fn admin_users(admin: Admin, conn: DbConn, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
render!(instance::users( render!(instance::users(
&(&*conn, &intl.catalog, Some(admin.0)), &(&*conn, &intl.catalog, Some(admin.0)),
User::get_local_page(&*conn, page.limits()), User::get_local_page(&*conn, page.limits()),
@ -202,7 +182,7 @@ pub fn ban(_admin: Admin, conn: DbConn, id: i32, searcher: Searcher) -> Redirect
if let Some(u) = User::get(&*conn, id) { if let Some(u) = User::get(&*conn, id) {
u.delete(&*conn, &searcher); u.delete(&*conn, &searcher);
} }
Redirect::to(uri!(admin_users)) Redirect::to(uri!(admin_users: page = _))
} }
#[post("/inbox", data = "<data>")] #[post("/inbox", data = "<data>")]

View file

@ -36,7 +36,7 @@ pub fn create(blog: String, slug: String, user: User, conn: DbConn, worker: Work
worker.execute(move || broadcast(&user, delete_act, dest)); worker.execute(move || broadcast(&user, delete_act, dest));
} }
Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug))) Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
} }
#[post("/~/<blog>/<slug>/like", rank = 2)] #[post("/~/<blog>/<slug>/like", rank = 2)]

View file

@ -1,6 +1,6 @@
use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder}; use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder};
use rocket::{ use rocket::{
http::RawStr, http::{RawStr, uri::{FromUriParam, Query}},
request::FromFormValue, request::FromFormValue,
response::NamedFile, response::NamedFile,
}; };
@ -10,7 +10,7 @@ use plume_models::{Connection, posts::Post};
const ITEMS_PER_PAGE: i32 = 12; const ITEMS_PER_PAGE: i32 = 12;
#[derive(Copy, Clone)] #[derive(Copy, Clone, UriDisplayQuery)]
pub struct Page(i32); pub struct Page(i32);
impl<'v> FromFormValue<'v> for Page { impl<'v> FromFormValue<'v> for Page {
@ -23,11 +23,15 @@ impl<'v> FromFormValue<'v> for Page {
} }
} }
impl Page { impl FromUriParam<Query, Option<Page>> for Page {
pub fn first() -> Page { type Target = Page;
Page(1)
fn from_uri_param(val: Option<Page>) -> Page {
val.unwrap_or_default()
}
} }
impl Page {
/// Computes the total number of pages needed to display n_items /// Computes the total number of pages needed to display n_items
pub fn total(n_items: i32) -> i32 { pub fn total(n_items: i32) -> i32 {
if n_items % ITEMS_PER_PAGE == 0 { if n_items % ITEMS_PER_PAGE == 0 {
@ -42,6 +46,12 @@ impl Page {
} }
} }
impl Default for Page {
fn default() -> Self {
Page(1)
}
}
pub fn post_to_atom(post: Post, conn: &Connection) -> Entry { pub fn post_to_atom(post: Post, conn: &Connection) -> Entry {
EntryBuilder::default() EntryBuilder::default()
.title(format!("<![CDATA[{}]]>", post.title)) .title(format!("<![CDATA[{}]]>", post.title))

View file

@ -7,7 +7,8 @@ use routes::Page;
use template_utils::Ructe; use template_utils::Ructe;
#[get("/notifications?<page>")] #[get("/notifications?<page>")]
pub fn paginated_notifications(conn: DbConn, user: User, page: Page, intl: I18n) -> Ructe { pub fn notifications(conn: DbConn, user: User, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
render!(notifications::index( render!(notifications::index(
&(&*conn, &intl.catalog, Some(user.clone())), &(&*conn, &intl.catalog, Some(user.clone())),
Notification::page_for_user(&*conn, &user, page.limits()), Notification::page_for_user(&*conn, &user, page.limits()),
@ -16,15 +17,10 @@ pub fn paginated_notifications(conn: DbConn, user: User, page: Page, intl: I18n)
)) ))
} }
#[get("/notifications")] #[get("/notifications?<page>", rank = 2)]
pub fn notifications(conn: DbConn, user: User, intl: I18n) -> Ructe { pub fn notifications_auth(i18n: I18n, page: Option<Page>) -> Flash<Redirect>{
paginated_notifications(conn, user, Page::first(), intl)
}
#[get("/notifications", rank = 2)]
pub fn notifications_auth(i18n: I18n) -> Flash<Redirect>{
utils::requires_login( utils::requires_login(
i18n!(i18n.catalog, "You need to be logged in order to see your notifications"), i18n!(i18n.catalog, "You need to be logged in order to see your notifications"),
uri!(notifications) uri!(notifications: page = page)
) )
} }

View file

@ -26,14 +26,8 @@ use template_utils::Ructe;
use Worker; use Worker;
use Searcher; use Searcher;
// See: https://github.com/SergioBenitez/Rocket/pull/454
#[get("/~/<blog>/<slug>", rank = 5)]
pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, intl: I18n) -> Result<Ructe, Ructe> {
details_response(blog, slug, conn, user, None, intl)
}
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)] #[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
pub fn details_response(blog: String, slug: String, conn: DbConn, user: Option<User>, responding_to: Option<i32>, intl: I18n) -> Result<Ructe, Ructe> { pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, responding_to: Option<i32>, intl: I18n) -> Result<Ructe, Ructe> {
let blog = Blog::find_by_fqn(&*conn, &blog).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?; let blog = Blog::find_by_fqn(&*conn, &blog).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?;
let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?; let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, user.clone()))))?;
if post.published || post.get_authors(&*conn).into_iter().any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)) { if post.published || post.get_authors(&*conn).into_iter().any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)) {
@ -205,7 +199,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
if errors.is_empty() { if errors.is_empty() {
if !user.is_author_in(&*conn, &b) { if !user.is_author_in(&*conn, &b) {
// actually it's not "Ok"… // actually it's not "Ok"…
Ok(Redirect::to(uri!(super::blogs::details: name = blog))) Ok(Redirect::to(uri!(super::blogs::details: name = blog, page = _)))
} else { } else {
let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref());
@ -252,7 +246,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien
} }
} }
Ok(Redirect::to(uri!(details: blog = blog, slug = new_slug))) Ok(Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)))
} }
} else { } else {
let medias = Media::for_user(&*conn, user.id); let medias = Media::for_user(&*conn, user.id);
@ -313,7 +307,7 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, con
if errors.is_empty() { if errors.is_empty() {
if !user.is_author_in(&*conn, &blog) { if !user.is_author_in(&*conn, &blog) {
// actually it's not "Ok"… // actually it's not "Ok"…
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name))) Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
} else { } else {
let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref());
@ -367,7 +361,7 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, con
worker.execute(move || broadcast(&user, act, dest)); worker.execute(move || broadcast(&user, act, dest));
} }
Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug))) Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)))
} }
} else { } else {
let medias = Media::for_user(&*conn, user.id); let medias = Media::for_user(&*conn, user.id);
@ -391,15 +385,15 @@ pub fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker:
if let Some(post) = post { if let Some(post) = post {
if !post.get_authors(&*conn).into_iter().any(|a| a.id == user.id) { if !post.get_authors(&*conn).into_iter().any(|a| a.id == user.id) {
Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone())) Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone(), responding_to = _))
} else { } else {
let dest = User::one_by_instance(&*conn); let dest = User::one_by_instance(&*conn);
let delete_activity = post.delete(&(&conn, &searcher)); let delete_activity = post.delete(&(&conn, &searcher));
worker.execute(move || broadcast(&user, delete_activity, dest)); worker.execute(move || broadcast(&user, delete_activity, dest));
Redirect::to(uri!(super::blogs::details: name = blog_name)) Redirect::to(uri!(super::blogs::details: name = blog_name, page = _))
} }
} else { } else {
Redirect::to(uri!(super::blogs::details: name = blog_name)) Redirect::to(uri!(super::blogs::details: name = blog_name, page = _))
} }
} }

View file

@ -37,7 +37,7 @@ pub fn create(blog: String, slug: String, user: User, conn: DbConn, worker: Work
worker.execute(move || broadcast(&user, delete_act, dest)); worker.execute(move || broadcast(&user, delete_act, dest));
} }
Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug))) Some(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
} }
#[post("/~/<blog>/<slug>/reshare", rank=1)] #[post("/~/<blog>/<slug>/reshare", rank=1)]

View file

@ -55,7 +55,7 @@ macro_rules! param_to_query {
#[get("/search?<query..>")] #[get("/search?<query..>")]
pub fn search(query: Form<SearchQuery>, conn: DbConn, searcher: Searcher, user: Option<User>, intl: I18n) -> Ructe { pub fn search(query: Form<SearchQuery>, conn: DbConn, searcher: Searcher, user: Option<User>, intl: I18n) -> Ructe {
let page = query.page.unwrap_or(Page::first()); let page = query.page.unwrap_or_default();
let mut parsed_query = Query::from_str(&query.q.as_ref().map(|q| q.as_str()).unwrap_or_default()); let mut parsed_query = Query::from_str(&query.q.as_ref().map(|q| q.as_str()).unwrap_or_default());
param_to_query!(query, parsed_query; normal: title, subtitle, content, tag, param_to_query!(query, parsed_query; normal: title, subtitle, content, tag,

View file

@ -14,27 +14,16 @@ use plume_models::{
users::{User, AUTH_COOKIE} users::{User, AUTH_COOKIE}
}; };
#[get("/login")]
pub fn new(user: Option<User>, conn: DbConn, intl: I18n) -> Ructe {
render!(session::login(
&(&*conn, &intl.catalog, user),
None,
&LoginForm::default(),
ValidationErrors::default()
))
}
#[get("/login?<m>")] #[get("/login?<m>")]
pub fn new_message(user: Option<User>, m: String, conn: DbConn, intl: I18n) -> Ructe { pub fn new(user: Option<User>, conn: DbConn, m: Option<String>, intl: I18n) -> Ructe {
render!(session::login( render!(session::login(
&(&*conn, &intl.catalog, user), &(&*conn, &intl.catalog, user),
Some(m), m,
&LoginForm::default(), &LoginForm::default(),
ValidationErrors::default() ValidationErrors::default()
)) ))
} }
#[derive(Default, FromForm, Validate, Serialize)] #[derive(Default, FromForm, Validate, Serialize)]
pub struct LoginForm { pub struct LoginForm {
#[validate(length(min = "1", message = "We need an email or a username to identify you"))] #[validate(length(min = "1", message = "We need an email or a username to identify you"))]

View file

@ -8,13 +8,9 @@ use plume_models::{
use routes::Page; use routes::Page;
use template_utils::Ructe; use template_utils::Ructe;
#[get("/tag/<name>")]
pub fn tag(user: Option<User>, conn: DbConn, name: String, intl: I18n) -> Ructe {
paginated_tag(user, conn, name, Page::first(), intl)
}
#[get("/tag/<name>?<page>")] #[get("/tag/<name>?<page>")]
pub fn paginated_tag(user: Option<User>, conn: DbConn, name: String, page: Page, intl: I18n) -> Ructe { pub fn tag(user: Option<User>, conn: DbConn, name: String, page: Option<Page>, intl: I18n) -> Ructe {
let page = page.unwrap_or_default();
let posts = Post::list_by_tag(&*conn, name.clone(), page.limits()); let posts = Post::list_by_tag(&*conn, name.clone(), page.limits());
render!(tags::index( render!(tags::index(
&(&*conn, &intl.catalog, user), &(&*conn, &intl.catalog, user),

View file

@ -165,7 +165,8 @@ pub fn follow_auth(name: String, i18n: I18n) -> Flash<Redirect> {
} }
#[get("/@/<name>/followers?<page>")] #[get("/@/<name>/followers?<page>")]
pub fn followers_paginated(name: String, conn: DbConn, account: Option<User>, page: Page, intl: I18n) -> Result<Ructe, Ructe> { pub fn followers(name: String, conn: DbConn, account: Option<User>, page: Option<Page>, intl: I18n) -> Result<Ructe, Ructe> {
let page = page.unwrap_or_default();
let user = User::find_by_fqn(&*conn, &name).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, account.clone()))))?; let user = User::find_by_fqn(&*conn, &name).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, account.clone()))))?;
let followers_count = user.get_followers(&*conn).len(); // TODO: count in DB let followers_count = user.get_followers(&*conn).len(); // TODO: count in DB
@ -181,11 +182,6 @@ pub fn followers_paginated(name: String, conn: DbConn, account: Option<User>, pa
))) )))
} }
#[get("/@/<name>/followers", rank = 2)]
pub fn followers(name: String, conn: DbConn, account: Option<User>, intl: I18n) -> Result<Ructe, Ructe> {
followers_paginated(name, conn, account, Page::first(), intl)
}
#[get("/@/<name>", rank = 1)] #[get("/@/<name>", rank = 1)]
pub fn activity_details( pub fn activity_details(
name: String, name: String,
@ -331,7 +327,7 @@ pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Resul
form.email.to_string(), form.email.to_string(),
User::hash_pass(&form.password), User::hash_pass(&form.password),
).update_boxes(&*conn); ).update_boxes(&*conn);
Redirect::to(uri!(super::session::new)) Redirect::to(uri!(super::session::new: m = _))
}) })
.map_err(|err| { .map_err(|err| {
render!(users::new( render!(users::new(

View file

@ -34,7 +34,7 @@
<i class="icon icon-home" aria-label="@i18n!(ctx.1, "Dashboard")"></i> <i class="icon icon-home" aria-label="@i18n!(ctx.1, "Dashboard")"></i>
<span class="mobile-label">@i18n!(ctx.1, "Dashboard")</span> <span class="mobile-label">@i18n!(ctx.1, "Dashboard")</span>
</a> </a>
<a href="@uri!(notifications::notifications)"> <a href="@uri!(notifications::notifications: page = _)">
<i class="icon icon-bell" aria-label="@i18n!(ctx.1, "Notifications")"></i> <i class="icon icon-bell" aria-label="@i18n!(ctx.1, "Notifications")"></i>
<span class="mobile-label">@i18n!(ctx.1, "Notifications")</span> <span class="mobile-label">@i18n!(ctx.1, "Notifications")</span>
</a> </a>
@ -47,7 +47,7 @@
<span class="mobile-label">@i18n!(ctx.1, "My account")</span> <span class="mobile-label">@i18n!(ctx.1, "My account")</span>
</a> </a>
} else { } else {
<a href="@uri!(session::new)"> <a href="@uri!(session::new: m = _)">
<i class="icon icon-log-in"></i> <i class="icon icon-log-in"></i>
<span class="mobile-label">@i18n!(ctx.1, "Log In")</span> <span class="mobile-label">@i18n!(ctx.1, "Log In")</span>
</a> </a>

View file

@ -8,7 +8,7 @@
@(ctx: BaseContext, blog: Blog, fqn: String, authors: &Vec<User>, total_articles: usize, page: i32, n_pages: i32, is_author: bool, posts: Vec<Post>) @(ctx: BaseContext, blog: Blog, fqn: String, authors: &Vec<User>, total_articles: usize, page: i32, n_pages: i32, is_author: bool, posts: Vec<Post>)
@:base(ctx, blog.title.as_ref(), {}, { @:base(ctx, blog.title.as_ref(), {}, {
<a href="@uri!(blogs::details: name = &fqn)">@blog.title</a> <a href="@uri!(blogs::details: name = &fqn, page = _)">@blog.title</a>
}, { }, {
<div class="hidden"> <div class="hidden">
@for author in authors { @for author in authors {

View file

@ -12,8 +12,8 @@
@tabs(&[ @tabs(&[
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), true), (&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), true),
(&uri!(instance::admin_instances).to_string(), i18n!(ctx.1, "Instances"), false), (&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
(&uri!(instance::admin_users).to_string(), i18n!(ctx.1, "Users"), false), (&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
]) ])
<form method="post" action="@uri!(instance::update_settings)"> <form method="post" action="@uri!(instance::update_settings)">

View file

@ -12,15 +12,15 @@
@if let Some(_) = ctx.2 { @if let Some(_) = ctx.2 {
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false),
(&uri!(instance::feed).to_string(), i18n!(ctx.1, "Your feed"), false), (&uri!(instance::feed: _).to_string(), i18n!(ctx.1, "Your feed"), false),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), true), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), true),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), false), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), false),
]) ])
} else { } else {
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), true), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), true),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), false), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), false),
]) ])
} }

View file

@ -10,9 +10,9 @@
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false),
(&uri!(instance::feed).to_string(), i18n!(ctx.1, "Your feed"), true), (&uri!(instance::feed: _).to_string(), i18n!(ctx.1, "Your feed"), true),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), false), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), false),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), false), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), false),
]) ])
@if !articles.is_empty() { @if !articles.is_empty() {

View file

@ -12,24 +12,24 @@
@if ctx.2.is_some() { @if ctx.2.is_some() {
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), true), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), true),
(&uri!(instance::feed).to_string(), i18n!(ctx.1, "Your feed"), false), (&uri!(instance::feed: _).to_string(), i18n!(ctx.1, "Your feed"), false),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), false), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), false),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), false), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), false),
]) ])
@:home_feed(ctx, user_feed.unwrap_or_default(), &uri!(instance::feed).to_string(), "Your feed") @:home_feed(ctx, user_feed.unwrap_or_default(), &uri!(instance::feed: _).to_string(), "Your feed")
@:home_feed(ctx, federated, &uri!(instance::federated).to_string(), "Federated feed") @:home_feed(ctx, federated, &uri!(instance::federated: _).to_string(), "Federated feed")
@:home_feed(ctx, local, &uri!(instance::local).to_string(), "Local feed") @:home_feed(ctx, local, &uri!(instance::local: _).to_string(), "Local feed")
@:instance_description(ctx, instance, n_users, n_articles) @:instance_description(ctx, instance, n_users, n_articles)
} else { } else {
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), true), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), true),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), false), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), false),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), false), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), false),
]) ])
@:home_feed(ctx, federated, &uri!(instance::federated).to_string(), "Federated feed") @:home_feed(ctx, federated, &uri!(instance::federated: _).to_string(), "Federated feed")
@:home_feed(ctx, local, &uri!(instance::local).to_string(), "Local feed") @:home_feed(ctx, local, &uri!(instance::local: _).to_string(), "Local feed")
@:instance_description(ctx, instance, n_users, n_articles) @:instance_description(ctx, instance, n_users, n_articles)
} }
}) })

View file

@ -10,8 +10,8 @@
@tabs(&[ @tabs(&[
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false), (&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
(&uri!(instance::admin_instances).to_string(), i18n!(ctx.1, "Instances"), true), (&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), true),
(&uri!(instance::admin_users).to_string(), i18n!(ctx.1, "Users"), false), (&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), false),
]) ])
<div class="list"> <div class="list">

View file

@ -13,15 +13,15 @@
@if let Some(_) = ctx.2 { @if let Some(_) = ctx.2 {
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false),
(&uri!(instance::feed).to_string(), i18n!(ctx.1, "Your feed"), false), (&uri!(instance::feed: _).to_string(), i18n!(ctx.1, "Your feed"), false),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), false), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), false),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), true), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), true),
]) ])
} else { } else {
@tabs(&[ @tabs(&[
(&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false), (&uri!(instance::index).to_string(), i18n!(ctx.1, "Latest articles"), false),
(&uri!(instance::federated).to_string(), i18n!(ctx.1, "Federated feed"), false), (&uri!(instance::federated: _).to_string(), i18n!(ctx.1, "Federated feed"), false),
(&uri!(instance::local).to_string(), i18n!(ctx.1, "Local feed"), true), (&uri!(instance::local: _).to_string(), i18n!(ctx.1, "Local feed"), true),
]) ])
} }

View file

@ -10,8 +10,8 @@
@tabs(&[ @tabs(&[
(&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false), (&uri!(instance::admin).to_string(), i18n!(ctx.1, "Configuration"), false),
(&uri!(instance::admin_instances).to_string(), i18n!(ctx.1, "Instances"), false), (&uri!(instance::admin_instances: page = _).to_string(), i18n!(ctx.1, "Instances"), false),
(&uri!(instance::admin_users).to_string(), i18n!(ctx.1, "Users"), true), (&uri!(instance::admin_users: page = _).to_string(), i18n!(ctx.1, "Users"), true),
]) ])
<div class="list"> <div class="list">

View file

@ -8,7 +8,11 @@
@if article.cover_id.is_some() { @if article.cover_id.is_some() {
<div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div> <div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div>
} }
<h3 class="p-name"><a class="u-url" href="@uri!(posts::details: blog = article.get_blog(ctx.0).get_fqn(ctx.0), slug = &article.slug)">@article.title</a></h3> <h3 class="p-name">
<a class="u-url" href="@uri!(posts::details: blog = article.get_blog(ctx.0).get_fqn(ctx.0), slug = &article.slug, responding_to = _)">
@article.title
</a>
</h3>
<main> <main>
<p class="p-summary">@article.subtitle</p> <p class="p-summary">@article.subtitle</p>
</main> </main>
@ -21,7 +25,7 @@
@if article.published { @if article.published {
<span class="dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span> <span class="dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span>
} }
<a href="@uri!(blogs::details: name = article.get_blog(ctx.0).get_fqn(ctx.0))">@article.get_blog(ctx.0).title</a> <a href="@uri!(blogs::details: name = article.get_blog(ctx.0).get_fqn(ctx.0), page = _)">@article.get_blog(ctx.0).title</a>
@if !article.published { @if !article.published {
⋅ @i18n!(ctx.1, "Draft") ⋅ @i18n!(ctx.1, "Draft")
} }

View file

@ -17,10 +17,10 @@
@if article.cover_id.is_some() { @if article.cover_id.is_some() {
<meta property="og:image" content="@Html(article.cover_url(ctx.0).unwrap_or_default())"/> <meta property="og:image" content="@Html(article.cover_url(ctx.0).unwrap_or_default())"/>
} }
<meta property="og:url" content="@uri!(posts::details: blog = blog.get_fqn(ctx.0), slug = &article.slug)"/> <meta property="og:url" content="@uri!(posts::details: blog = blog.get_fqn(ctx.0), slug = &article.slug, responding_to = _)"/>
<meta property="og:description" content="@article.subtitle"/> <meta property="og:description" content="@article.subtitle"/>
}, { }, {
<a href="@uri!(blogs::details: name = blog.get_fqn(ctx.0))">@blog.title</a> <a href="@uri!(blogs::details: name = blog.get_fqn(ctx.0), page = _)">@blog.title</a>
}, { }, {
<div class="h-entry"> <div class="h-entry">
<h1 class="article p-name">@&article.title</h1> <h1 class="article p-name">@&article.title</h1>
@ -63,7 +63,7 @@
<ul class="tags"> <ul class="tags">
@for tag in tags { @for tag in tags {
@if !tag.is_hashtag { @if !tag.is_hashtag {
<li><a class="p-category" href="@uri!(tags::tag: name = &tag.tag)">@tag.tag</a></li> <li><a class="p-category" href="@uri!(tags::tag: name = &tag.tag, page = _)">@tag.tag</a></li>
} else { } else {
<span class="hidden p-category">@tag.tag</span> <span class="hidden p-category">@tag.tag</span>
} }
@ -116,14 +116,14 @@
<p aria-label="@i18n!(ctx.1, "One like", "{0} likes", &n_likes)" title="@i18n!(ctx.1, "One like", "{0} likes", n_likes)"> <p aria-label="@i18n!(ctx.1, "One like", "{0} likes", &n_likes)" title="@i18n!(ctx.1, "One like", "{0} likes", n_likes)">
@n_likes @n_likes
</p> </p>
<a href="@uri!(session::new_message: m = i18n!(ctx.1, "Login to like"))" class="action">@icon!("heart") @i18n!(ctx.1, "Add yours")</a> <a href="@uri!(session::new: m = i18n!(ctx.1, "Login to like"))" class="action">@icon!("heart") @i18n!(ctx.1, "Add yours")</a>
</div> </div>
<div class="reshares"> <div class="reshares">
<p aria-label="@i18n!(ctx.1, "One boost", "{0} boost", &n_reshares)" title="@i18n!(ctx.1, "One boost", "{0} boosts", n_reshares)"> <p aria-label="@i18n!(ctx.1, "One boost", "{0} boost", &n_reshares)" title="@i18n!(ctx.1, "One boost", "{0} boosts", n_reshares)">
@n_reshares @n_reshares
</p> </p>
<a href="@uri!(session::new_message: m = i18n!(ctx.1, "Login to boost"))" class="action">@icon!("repeat") @i18n!(ctx.1, "Boost")</a> <a href="@uri!(session::new: m = i18n!(ctx.1, "Login to boost"))" class="action">@icon!("repeat") @i18n!(ctx.1, "Boost")</a>
</div> </div>
</div> </div>
} }

View file

@ -17,7 +17,7 @@
<div class="cards"> <div class="cards">
@for blog in blogs { @for blog in blogs {
<div class="card"> <div class="card">
<h3><a href="@uri!(blogs::details: name = blog.actor_id)">@blog.title</a></h3> <h3><a href="@uri!(blogs::details: name = blog.actor_id, page = _)">@blog.title</a></h3>
</div> </div>
} }
</div> </div>

View file

@ -11,7 +11,7 @@
@tabs(&[ @tabs(&[
(&uri!(user::details: name= user.get_fqn(ctx.0)).to_string(), i18n!(ctx.1, "Articles"), true), (&uri!(user::details: name= user.get_fqn(ctx.0)).to_string(), i18n!(ctx.1, "Articles"), true),
(&uri!(user::followers: name = user.get_fqn(ctx.0)).to_string(), i18n!(ctx.1, "Followers"), false) (&uri!(user::followers: name = user.get_fqn(ctx.0), page = _).to_string(), i18n!(ctx.1, "Followers"), false)
]) ])
@if !recents.is_empty() { @if !recents.is_empty() {

View file

@ -10,7 +10,7 @@
@tabs(&[ @tabs(&[
(&uri!(user::details: name= user.get_fqn(ctx.0)).to_string(), i18n!(ctx.1, "Articles"), false), (&uri!(user::details: name= user.get_fqn(ctx.0)).to_string(), i18n!(ctx.1, "Articles"), false),
(&uri!(user::followers: name = user.get_fqn(ctx.0)).to_string(), i18n!(ctx.1, "Followers"), true) (&uri!(user::followers: name = user.get_fqn(ctx.0), page = _).to_string(), i18n!(ctx.1, "Followers"), true)
]) ])
<div class="cards"> <div class="cards">