]> Untitled Git - lemmy.git/commitdiff
Adding stickied posts.
authorDessalines <tyhou13@gmx.com>
Mon, 9 Sep 2019 06:14:13 +0000 (23:14 -0700)
committerDessalines <tyhou13@gmx.com>
Mon, 9 Sep 2019 06:14:13 +0000 (23:14 -0700)
- Fixes #245

15 files changed:
server/migrations/2019-09-09-042010_add_stickied_posts/down.sql [new file with mode: 0644]
server/migrations/2019-09-09-042010_add_stickied_posts/up.sql [new file with mode: 0644]
server/src/api/post.rs
server/src/api/site.rs
server/src/db/comment.rs
server/src/db/comment_view.rs
server/src/db/moderator.rs
server/src/db/moderator_views.rs
server/src/db/post.rs
server/src/db/post_view.rs
server/src/schema.rs
ui/src/components/modlog.tsx
ui/src/components/post-listing.tsx
ui/src/interfaces.ts
ui/src/translations/en.ts

diff --git a/server/migrations/2019-09-09-042010_add_stickied_posts/down.sql b/server/migrations/2019-09-09-042010_add_stickied_posts/down.sql
new file mode 100644 (file)
index 0000000..fb8eac8
--- /dev/null
@@ -0,0 +1,50 @@
+drop view post_view;
+drop view mod_sticky_post_view;
+alter table post drop column stickied;
+
+drop table mod_sticky_post;
+
+create view post_view as
+with all_post as
+(
+  select        
+  p.*,
+  (select u.banned from user_ u where p.creator_id = u.id) as banned,
+  (select cb.id::bool from community_user_ban cb where p.creator_id = cb.user_id and p.community_id = cb.community_id) as banned_from_community,
+  (select name from user_ where p.creator_id = user_.id) as creator_name,
+  (select name from community where p.community_id = community.id) as community_name,
+  (select removed from community c where p.community_id = c.id) as community_removed,
+  (select deleted from community c where p.community_id = c.id) as community_deleted,
+  (select nsfw from community c where p.community_id = c.id) as community_nsfw,
+  (select count(*) from comment where comment.post_id = p.id) as number_of_comments,
+  coalesce(sum(pl.score), 0) as score,
+  count (case when pl.score = 1 then 1 else null end) as upvotes,
+  count (case when pl.score = -1 then 1 else null end) as downvotes,
+  hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
+  from post p
+  left join post_like pl on p.id = pl.post_id
+  group by p.id
+)
+
+select
+ap.*,
+u.id as user_id,
+coalesce(pl.score, 0) as my_vote,
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
+(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
+from user_ u
+cross join all_post ap
+left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
+
+union all
+
+select 
+ap.*,
+null as user_id,
+null as my_vote,
+null as subscribed,
+null as read,
+null as saved
+from all_post ap
+;
diff --git a/server/migrations/2019-09-09-042010_add_stickied_posts/up.sql b/server/migrations/2019-09-09-042010_add_stickied_posts/up.sql
new file mode 100644 (file)
index 0000000..0848f86
--- /dev/null
@@ -0,0 +1,67 @@
+-- Add the column
+alter table post add column stickied boolean default false not null;
+
+-- Add the mod table
+create table mod_sticky_post (
+  id serial primary key,
+  mod_user_id int references user_ on update cascade on delete cascade not null,
+  post_id int references post on update cascade on delete cascade not null,
+  stickied boolean default true,
+  when_ timestamp not null default now()
+);
+
+-- Add mod view
+create view mod_sticky_post_view as 
+select msp.*,
+(select name from user_ u where msp.mod_user_id = u.id) as mod_user_name,
+(select name from post p where msp.post_id = p.id) as post_name,
+(select c.id from post p, community c where msp.post_id = p.id and p.community_id = c.id) as community_id,
+(select c.name from post p, community c where msp.post_id = p.id and p.community_id = c.id) as community_name
+from mod_sticky_post msp;
+
+-- Recreate the view
+drop view post_view;
+create view post_view as
+with all_post as
+(
+  select        
+  p.*,
+  (select u.banned from user_ u where p.creator_id = u.id) as banned,
+  (select cb.id::bool from community_user_ban cb where p.creator_id = cb.user_id and p.community_id = cb.community_id) as banned_from_community,
+  (select name from user_ where p.creator_id = user_.id) as creator_name,
+  (select name from community where p.community_id = community.id) as community_name,
+  (select removed from community c where p.community_id = c.id) as community_removed,
+  (select deleted from community c where p.community_id = c.id) as community_deleted,
+  (select nsfw from community c where p.community_id = c.id) as community_nsfw,
+  (select count(*) from comment where comment.post_id = p.id) as number_of_comments,
+  coalesce(sum(pl.score), 0) as score,
+  count (case when pl.score = 1 then 1 else null end) as upvotes,
+  count (case when pl.score = -1 then 1 else null end) as downvotes,
+  hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
+  from post p
+  left join post_like pl on p.id = pl.post_id
+  group by p.id
+)
+
+select
+ap.*,
+u.id as user_id,
+coalesce(pl.score, 0) as my_vote,
+(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
+(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
+(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
+from user_ u
+cross join all_post ap
+left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
+
+union all
+
+select 
+ap.*,
+null as user_id,
+null as my_vote,
+null as subscribed,
+null as read,
+null as saved
+from all_post ap
+;
index 5345b0e4e066042797026badf38fdb19449c2433..d53d2064ac772b97d9345ee28bc3a7501e18fe73 100644 (file)
@@ -74,6 +74,7 @@ pub struct EditPost {
   deleted: Option<bool>,
   nsfw: bool,
   locked: Option<bool>,
+  stickied: Option<bool>,
   reason: Option<String>,
   auth: String,
 }
@@ -121,6 +122,7 @@ impl Perform<PostResponse> for Oper<CreatePost> {
       deleted: None,
       nsfw: data.nsfw,
       locked: None,
+      stickied: None,
       updated: None,
     };
 
@@ -365,6 +367,7 @@ impl Perform<PostResponse> for Oper<EditPost> {
       deleted: data.deleted.to_owned(),
       nsfw: data.nsfw,
       locked: data.locked.to_owned(),
+      stickied: data.stickied.to_owned(),
       updated: Some(naive_now()),
     };
 
@@ -393,6 +396,15 @@ impl Perform<PostResponse> for Oper<EditPost> {
       ModLockPost::create(&conn, &form)?;
     }
 
+    if let Some(stickied) = data.stickied.to_owned() {
+      let form = ModStickyPostForm {
+        mod_user_id: user_id,
+        post_id: data.edit_id,
+        stickied: Some(stickied),
+      };
+      ModStickyPost::create(&conn, &form)?;
+    }
+
     let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
 
     Ok(PostResponse {
index bbc26dab3cfcee0c06b2184c1b5f21ab963a0e7b..b517568e6262b9ea2f3d63d3c08795aa2433d8b4 100644 (file)
@@ -43,6 +43,7 @@ pub struct GetModlogResponse {
   op: String,
   removed_posts: Vec<ModRemovePostView>,
   locked_posts: Vec<ModLockPostView>,
+  stickied_posts: Vec<ModStickyPostView>,
   removed_comments: Vec<ModRemoveCommentView>,
   removed_communities: Vec<ModRemoveCommunityView>,
   banned_from_community: Vec<ModBanFromCommunityView>,
@@ -122,6 +123,13 @@ impl Perform<GetModlogResponse> for Oper<GetModlog> {
       data.page,
       data.limit,
     )?;
+    let stickied_posts = ModStickyPostView::list(
+      &conn,
+      data.community_id,
+      data.mod_user_id,
+      data.page,
+      data.limit,
+    )?;
     let removed_comments = ModRemoveCommentView::list(
       &conn,
       data.community_id,
@@ -161,6 +169,7 @@ impl Perform<GetModlogResponse> for Oper<GetModlog> {
       op: self.op.to_string(),
       removed_posts: removed_posts,
       locked_posts: locked_posts,
+      stickied_posts: stickied_posts,
       removed_comments: removed_comments,
       removed_communities: removed_communities,
       banned_from_community: banned_from_community,
index ec00fab79f77044665ae40f39fd3044d41a25f98..f62f8c536a0923a5018d0023a3eb04d6daab9367 100644 (file)
@@ -205,6 +205,7 @@ mod tests {
       removed: None,
       deleted: None,
       locked: None,
+      stickied: None,
       updated: None,
       nsfw: false,
     };
index 81f7a6721666acb5b42b93ce2101e2f78e47f7de..6c2e3f59306076212807a7a1fc513deb51a5474c 100644 (file)
@@ -291,6 +291,7 @@ mod tests {
       removed: None,
       deleted: None,
       locked: None,
+      stickied: None,
       updated: None,
       nsfw: false,
     };
index d1e5cb725ab872f9fae095bf1a7a08feb0002110..117e3f553db0256618a1ffb19e01a81fbc49b589 100644 (file)
@@ -1,7 +1,7 @@
 use super::*;
 use crate::schema::{
   mod_add, mod_add_community, mod_ban, mod_ban_from_community, mod_lock_post, mod_remove_comment,
-  mod_remove_community, mod_remove_post,
+  mod_remove_community, mod_remove_post, mod_sticky_post,
 };
 
 #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
@@ -94,6 +94,50 @@ impl Crud<ModLockPostForm> for ModLockPost {
   }
 }
 
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name = "mod_sticky_post"]
+pub struct ModStickyPost {
+  pub id: i32,
+  pub mod_user_id: i32,
+  pub post_id: i32,
+  pub stickied: Option<bool>,
+  pub when_: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
+#[table_name = "mod_sticky_post"]
+pub struct ModStickyPostForm {
+  pub mod_user_id: i32,
+  pub post_id: i32,
+  pub stickied: Option<bool>,
+}
+
+impl Crud<ModStickyPostForm> for ModStickyPost {
+  fn read(conn: &PgConnection, from_id: i32) -> Result<Self, Error> {
+    use crate::schema::mod_sticky_post::dsl::*;
+    mod_sticky_post.find(from_id).first::<Self>(conn)
+  }
+
+  fn delete(conn: &PgConnection, from_id: i32) -> Result<usize, Error> {
+    use crate::schema::mod_sticky_post::dsl::*;
+    diesel::delete(mod_sticky_post.find(from_id)).execute(conn)
+  }
+
+  fn create(conn: &PgConnection, form: &ModStickyPostForm) -> Result<Self, Error> {
+    use crate::schema::mod_sticky_post::dsl::*;
+    insert_into(mod_sticky_post)
+      .values(form)
+      .get_result::<Self>(conn)
+  }
+
+  fn update(conn: &PgConnection, from_id: i32, form: &ModStickyPostForm) -> Result<Self, Error> {
+    use crate::schema::mod_sticky_post::dsl::*;
+    diesel::update(mod_sticky_post.find(from_id))
+      .set(form)
+      .get_result::<Self>(conn)
+  }
+}
+
 #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
 #[table_name = "mod_remove_comment"]
 pub struct ModRemoveComment {
@@ -443,6 +487,7 @@ mod tests {
       removed: None,
       deleted: None,
       locked: None,
+      stickied: None,
       updated: None,
       nsfw: false,
     };
@@ -472,9 +517,8 @@ mod tests {
       removed: None,
     };
     let inserted_mod_remove_post = ModRemovePost::create(&conn, &mod_remove_post_form).unwrap();
-    let read_moderator_remove_post =
-      ModRemovePost::read(&conn, inserted_mod_remove_post.id).unwrap();
-    let expected_moderator_remove_post = ModRemovePost {
+    let read_mod_remove_post = ModRemovePost::read(&conn, inserted_mod_remove_post.id).unwrap();
+    let expected_mod_remove_post = ModRemovePost {
       id: inserted_mod_remove_post.id,
       post_id: inserted_post.id,
       mod_user_id: inserted_mod.id,
@@ -491,8 +535,8 @@ mod tests {
       locked: None,
     };
     let inserted_mod_lock_post = ModLockPost::create(&conn, &mod_lock_post_form).unwrap();
-    let read_moderator_lock_post = ModLockPost::read(&conn, inserted_mod_lock_post.id).unwrap();
-    let expected_moderator_lock_post = ModLockPost {
+    let read_mod_lock_post = ModLockPost::read(&conn, inserted_mod_lock_post.id).unwrap();
+    let expected_mod_lock_post = ModLockPost {
       id: inserted_mod_lock_post.id,
       post_id: inserted_post.id,
       mod_user_id: inserted_mod.id,
@@ -500,6 +544,23 @@ mod tests {
       when_: inserted_mod_lock_post.when_,
     };
 
+    // sticky post
+
+    let mod_sticky_post_form = ModStickyPostForm {
+      mod_user_id: inserted_mod.id,
+      post_id: inserted_post.id,
+      stickied: None,
+    };
+    let inserted_mod_sticky_post = ModStickyPost::create(&conn, &mod_sticky_post_form).unwrap();
+    let read_mod_sticky_post = ModStickyPost::read(&conn, inserted_mod_sticky_post.id).unwrap();
+    let expected_mod_sticky_post = ModStickyPost {
+      id: inserted_mod_sticky_post.id,
+      post_id: inserted_post.id,
+      mod_user_id: inserted_mod.id,
+      stickied: Some(true),
+      when_: inserted_mod_sticky_post.when_,
+    };
+
     // comment
 
     let mod_remove_comment_form = ModRemoveCommentForm {
@@ -510,9 +571,9 @@ mod tests {
     };
     let inserted_mod_remove_comment =
       ModRemoveComment::create(&conn, &mod_remove_comment_form).unwrap();
-    let read_moderator_remove_comment =
+    let read_mod_remove_comment =
       ModRemoveComment::read(&conn, inserted_mod_remove_comment.id).unwrap();
-    let expected_moderator_remove_comment = ModRemoveComment {
+    let expected_mod_remove_comment = ModRemoveComment {
       id: inserted_mod_remove_comment.id,
       comment_id: inserted_comment.id,
       mod_user_id: inserted_mod.id,
@@ -532,9 +593,9 @@ mod tests {
     };
     let inserted_mod_remove_community =
       ModRemoveCommunity::create(&conn, &mod_remove_community_form).unwrap();
-    let read_moderator_remove_community =
+    let read_mod_remove_community =
       ModRemoveCommunity::read(&conn, inserted_mod_remove_community.id).unwrap();
-    let expected_moderator_remove_community = ModRemoveCommunity {
+    let expected_mod_remove_community = ModRemoveCommunity {
       id: inserted_mod_remove_community.id,
       community_id: inserted_community.id,
       mod_user_id: inserted_mod.id,
@@ -556,9 +617,9 @@ mod tests {
     };
     let inserted_mod_ban_from_community =
       ModBanFromCommunity::create(&conn, &mod_ban_from_community_form).unwrap();
-    let read_moderator_ban_from_community =
+    let read_mod_ban_from_community =
       ModBanFromCommunity::read(&conn, inserted_mod_ban_from_community.id).unwrap();
-    let expected_moderator_ban_from_community = ModBanFromCommunity {
+    let expected_mod_ban_from_community = ModBanFromCommunity {
       id: inserted_mod_ban_from_community.id,
       community_id: inserted_community.id,
       mod_user_id: inserted_mod.id,
@@ -579,8 +640,8 @@ mod tests {
       expires: None,
     };
     let inserted_mod_ban = ModBan::create(&conn, &mod_ban_form).unwrap();
-    let read_moderator_ban = ModBan::read(&conn, inserted_mod_ban.id).unwrap();
-    let expected_moderator_ban = ModBan {
+    let read_mod_ban = ModBan::read(&conn, inserted_mod_ban.id).unwrap();
+    let expected_mod_ban = ModBan {
       id: inserted_mod_ban.id,
       mod_user_id: inserted_mod.id,
       other_user_id: inserted_user.id,
@@ -600,9 +661,9 @@ mod tests {
     };
     let inserted_mod_add_community =
       ModAddCommunity::create(&conn, &mod_add_community_form).unwrap();
-    let read_moderator_add_community =
+    let read_mod_add_community =
       ModAddCommunity::read(&conn, inserted_mod_add_community.id).unwrap();
-    let expected_moderator_add_community = ModAddCommunity {
+    let expected_mod_add_community = ModAddCommunity {
       id: inserted_mod_add_community.id,
       community_id: inserted_community.id,
       mod_user_id: inserted_mod.id,
@@ -619,8 +680,8 @@ mod tests {
       removed: None,
     };
     let inserted_mod_add = ModAdd::create(&conn, &mod_add_form).unwrap();
-    let read_moderator_add = ModAdd::read(&conn, inserted_mod_add.id).unwrap();
-    let expected_moderator_add = ModAdd {
+    let read_mod_add = ModAdd::read(&conn, inserted_mod_add.id).unwrap();
+    let expected_mod_add = ModAdd {
       id: inserted_mod_add.id,
       mod_user_id: inserted_mod.id,
       other_user_id: inserted_user.id,
@@ -630,6 +691,7 @@ mod tests {
 
     ModRemovePost::delete(&conn, inserted_mod_remove_post.id).unwrap();
     ModLockPost::delete(&conn, inserted_mod_lock_post.id).unwrap();
+    ModStickyPost::delete(&conn, inserted_mod_sticky_post.id).unwrap();
     ModRemoveComment::delete(&conn, inserted_mod_remove_comment.id).unwrap();
     ModRemoveCommunity::delete(&conn, inserted_mod_remove_community.id).unwrap();
     ModBanFromCommunity::delete(&conn, inserted_mod_ban_from_community.id).unwrap();
@@ -643,25 +705,14 @@ mod tests {
     User_::delete(&conn, inserted_user.id).unwrap();
     User_::delete(&conn, inserted_mod.id).unwrap();
 
-    assert_eq!(expected_moderator_remove_post, read_moderator_remove_post);
-    assert_eq!(expected_moderator_lock_post, read_moderator_lock_post);
-    assert_eq!(
-      expected_moderator_remove_comment,
-      read_moderator_remove_comment
-    );
-    assert_eq!(
-      expected_moderator_remove_community,
-      read_moderator_remove_community
-    );
-    assert_eq!(
-      expected_moderator_ban_from_community,
-      read_moderator_ban_from_community
-    );
-    assert_eq!(expected_moderator_ban, read_moderator_ban);
-    assert_eq!(
-      expected_moderator_add_community,
-      read_moderator_add_community
-    );
-    assert_eq!(expected_moderator_add, read_moderator_add);
+    assert_eq!(expected_mod_remove_post, read_mod_remove_post);
+    assert_eq!(expected_mod_lock_post, read_mod_lock_post);
+    assert_eq!(expected_mod_sticky_post, read_mod_sticky_post);
+    assert_eq!(expected_mod_remove_comment, read_mod_remove_comment);
+    assert_eq!(expected_mod_remove_community, read_mod_remove_community);
+    assert_eq!(expected_mod_ban_from_community, read_mod_ban_from_community);
+    assert_eq!(expected_mod_ban, read_mod_ban);
+    assert_eq!(expected_mod_add_community, read_mod_add_community);
+    assert_eq!(expected_mod_add, read_mod_add);
   }
 }
index b8143b50461035f71ee84f27d9504e320be04f13..0413e0b10b7a104eea72b9bae49cf0d90e4dcb42 100644 (file)
@@ -120,6 +120,65 @@ impl ModLockPostView {
   }
 }
 
+table! {
+  mod_sticky_post_view (id) {
+    id -> Int4,
+    mod_user_id -> Int4,
+    post_id -> Int4,
+    stickied -> Nullable<Bool>,
+    when_ -> Timestamp,
+    mod_user_name -> Varchar,
+    post_name -> Varchar,
+    community_id -> Int4,
+    community_name -> Varchar,
+  }
+}
+
+#[derive(
+  Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
+)]
+#[table_name = "mod_sticky_post_view"]
+pub struct ModStickyPostView {
+  pub id: i32,
+  pub mod_user_id: i32,
+  pub post_id: i32,
+  pub stickied: Option<bool>,
+  pub when_: chrono::NaiveDateTime,
+  pub mod_user_name: String,
+  pub post_name: String,
+  pub community_id: i32,
+  pub community_name: String,
+}
+
+impl ModStickyPostView {
+  pub fn list(
+    conn: &PgConnection,
+    from_community_id: Option<i32>,
+    from_mod_user_id: Option<i32>,
+    page: Option<i64>,
+    limit: Option<i64>,
+  ) -> Result<Vec<Self>, Error> {
+    use super::moderator_views::mod_sticky_post_view::dsl::*;
+    let mut query = mod_sticky_post_view.into_boxed();
+
+    let (limit, offset) = limit_and_offset(page, limit);
+
+    if let Some(from_community_id) = from_community_id {
+      query = query.filter(community_id.eq(from_community_id));
+    };
+
+    if let Some(from_mod_user_id) = from_mod_user_id {
+      query = query.filter(mod_user_id.eq(from_mod_user_id));
+    };
+
+    query
+      .limit(limit)
+      .offset(offset)
+      .order_by(when_.desc())
+      .load::<Self>(conn)
+  }
+}
+
 table! {
   mod_remove_comment_view (id) {
     id -> Int4,
index 54cc236b569f284b8abab659f58d25120659ee6e..2ae8bf68fb158c5ab86ca37a3148404595bda6aa 100644 (file)
@@ -16,6 +16,7 @@ pub struct Post {
   pub updated: Option<chrono::NaiveDateTime>,
   pub deleted: bool,
   pub nsfw: bool,
+  pub stickied: bool,
 }
 
 #[derive(Insertable, AsChangeset, Clone)]
@@ -31,6 +32,7 @@ pub struct PostForm {
   pub updated: Option<chrono::NaiveDateTime>,
   pub deleted: Option<bool>,
   pub nsfw: bool,
+  pub stickied: Option<bool>,
 }
 
 impl Crud<PostForm> for Post {
@@ -216,6 +218,7 @@ mod tests {
       removed: None,
       deleted: None,
       locked: None,
+      stickied: None,
       nsfw: false,
       updated: None,
     };
@@ -232,6 +235,7 @@ mod tests {
       published: inserted_post.published,
       removed: false,
       locked: false,
+      stickied: false,
       nsfw: false,
       deleted: false,
       updated: None,
index 2008d16f1f71433d7493fc5b8d5e9c2b183ed1b5..6d06ce8ec51ca17ea9f60237ae8d75ca593aa659 100644 (file)
@@ -24,6 +24,7 @@ table! {
     nsfw -> Bool,
     banned -> Bool,
     banned_from_community -> Bool,
+    stickied -> Bool,
     creator_name -> Varchar,
     community_name -> Varchar,
     community_removed -> Bool,
@@ -61,6 +62,7 @@ pub struct PostView {
   pub nsfw: bool,
   pub banned: bool,
   pub banned_from_community: bool,
+  pub stickied: bool,
   pub creator_name: String,
   pub community_name: String,
   pub community_removed: bool,
@@ -100,10 +102,6 @@ impl PostView {
 
     let mut query = post_view.into_boxed();
 
-    if let Some(for_community_id) = for_community_id {
-      query = query.filter(community_id.eq(for_community_id));
-    };
-
     if let Some(for_creator_id) = for_creator_id {
       query = query.filter(creator_id.eq(for_creator_id));
     };
@@ -116,6 +114,11 @@ impl PostView {
       query = query.filter(url.eq(url_search));
     };
 
+    if let Some(for_community_id) = for_community_id {
+      query = query.filter(community_id.eq(for_community_id));
+      query = query.then_order_by(stickied.desc());
+    };
+
     // TODO these are wrong, bc they'll only show saved for your logged in user, not theirs
     if saved_only {
       query = query.filter(saved.eq(true));
@@ -147,22 +150,22 @@ impl PostView {
 
     query = match sort {
       SortType::Hot => query
-        .order_by(hot_rank.desc())
+        .then_order_by(hot_rank.desc())
         .then_order_by(published.desc()),
-      SortType::New => query.order_by(published.desc()),
-      SortType::TopAll => query.order_by(score.desc()),
+      SortType::New => query.then_order_by(published.desc()),
+      SortType::TopAll => query.then_order_by(score.desc()),
       SortType::TopYear => query
         .filter(published.gt(now - 1.years()))
-        .order_by(score.desc()),
+        .then_order_by(score.desc()),
       SortType::TopMonth => query
         .filter(published.gt(now - 1.months()))
-        .order_by(score.desc()),
+        .then_order_by(score.desc()),
       SortType::TopWeek => query
         .filter(published.gt(now - 1.weeks()))
-        .order_by(score.desc()),
+        .then_order_by(score.desc()),
       SortType::TopDay => query
         .filter(published.gt(now - 1.days()))
-        .order_by(score.desc()),
+        .then_order_by(score.desc()),
     };
 
     query = query
@@ -249,6 +252,7 @@ mod tests {
       removed: None,
       deleted: None,
       locked: None,
+      stickied: None,
       updated: None,
       nsfw: false,
     };
@@ -293,6 +297,7 @@ mod tests {
       removed: false,
       deleted: false,
       locked: false,
+      stickied: false,
       community_name: community_name.to_owned(),
       community_removed: false,
       community_deleted: false,
@@ -320,6 +325,7 @@ mod tests {
       removed: false,
       deleted: false,
       locked: false,
+      stickied: false,
       creator_id: inserted_user.id,
       creator_name: user_name.to_owned(),
       banned: false,
index 2ea774ca007e2f9ee08649caa89a9e290158498c..0e1a33ef69c677f4a8dd6b56f8a5bd8eb9f020ca 100644 (file)
@@ -173,6 +173,16 @@ table! {
     }
 }
 
+table! {
+    mod_sticky_post (id) {
+        id -> Int4,
+        mod_user_id -> Int4,
+        post_id -> Int4,
+        stickied -> Nullable<Bool>,
+        when_ -> Timestamp,
+    }
+}
+
 table! {
     post (id) {
         id -> Int4,
@@ -187,6 +197,7 @@ table! {
         updated -> Nullable<Timestamp>,
         deleted -> Bool,
         nsfw -> Bool,
+        stickied -> Bool,
     }
 }
 
@@ -279,6 +290,8 @@ joinable!(mod_remove_community -> community (community_id));
 joinable!(mod_remove_community -> user_ (mod_user_id));
 joinable!(mod_remove_post -> post (post_id));
 joinable!(mod_remove_post -> user_ (mod_user_id));
+joinable!(mod_sticky_post -> post (post_id));
+joinable!(mod_sticky_post -> user_ (mod_user_id));
 joinable!(post -> community (community_id));
 joinable!(post -> user_ (creator_id));
 joinable!(post_like -> post (post_id));
@@ -307,6 +320,7 @@ allow_tables_to_appear_in_same_query!(
   mod_remove_comment,
   mod_remove_community,
   mod_remove_post,
+  mod_sticky_post,
   post,
   post_like,
   post_read,
index ba1fe5a21d49cb0f3885831664ddc77219203ef5..3af122a8e6a346a7f497fe5e690a450ea451fdca 100644 (file)
@@ -2,14 +2,15 @@ import { Component, linkEvent } from 'inferno';
 import { Link } from 'inferno-router';
 import { Subscription } from "rxjs";
 import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, GetModlogForm, GetModlogResponse, ModRemovePost, ModLockPost, ModRemoveComment, ModRemoveCommunity, ModBanFromCommunity, ModBan, ModAddCommunity, ModAdd } from '../interfaces';
+import { UserOperation, GetModlogForm, GetModlogResponse, ModRemovePost, ModLockPost, ModStickyPost, ModRemoveComment, ModRemoveCommunity, ModBanFromCommunity, ModBan, ModAddCommunity, ModAdd } from '../interfaces';
 import { WebSocketService } from '../services';
 import { msgOp, addTypeInfo, fetchLimit } from '../utils';
 import { MomentTime } from './moment-time';
 import * as moment from 'moment';
+import { i18n } from '../i18next';
 
 interface ModlogState {
-  combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModRemoveCommunity | ModAdd | ModBan}>,
+  combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModStickyPost | ModRemoveCommunity | ModAdd | ModBan}>,
   communityId?: number,
   communityName?: string,
   page: number;
@@ -51,6 +52,7 @@ export class Modlog extends Component<any, ModlogState> {
   setCombined(res: GetModlogResponse) {
     let removed_posts = addTypeInfo(res.removed_posts, "removed_posts");
     let locked_posts = addTypeInfo(res.locked_posts, "locked_posts");
+    let stickied_posts = addTypeInfo(res.stickied_posts, "stickied_posts");
     let removed_comments = addTypeInfo(res.removed_comments, "removed_comments");
     let removed_communities = addTypeInfo(res.removed_communities, "removed_communities");
     let banned_from_community = addTypeInfo(res.banned_from_community, "banned_from_community");
@@ -61,6 +63,7 @@ export class Modlog extends Component<any, ModlogState> {
 
     this.state.combined.push(...removed_posts);
     this.state.combined.push(...locked_posts);
+    this.state.combined.push(...stickied_posts);
     this.state.combined.push(...removed_comments);
     this.state.combined.push(...removed_communities);
     this.state.combined.push(...banned_from_community);
@@ -99,6 +102,12 @@ export class Modlog extends Component<any, ModlogState> {
                   <span> Post <Link to={`/post/${(i.data as ModLockPost).post_id}`}>{(i.data as ModLockPost).post_name}</Link></span>
                 </>
               }
+              {i.type_ == 'stickied_posts' && 
+                <>
+                  {(i.data as ModStickyPost).stickied? 'Stickied' : 'Unstickied'} 
+                  <span> Post <Link to={`/post/${(i.data as ModStickyPost).post_id}`}>{(i.data as ModStickyPost).post_name}</Link></span>
+                </>
+              }
               {i.type_ == 'removed_comments' && 
                 <>
                   {(i.data as ModRemoveComment).removed? 'Removed' : 'Restored'} 
index 8234cc8a6edbda9d3d6a5f11f4c2ab9856bd1754..94cbef106d2ddbaf1315b9b1ddc46c3ac9712dcf 100644 (file)
@@ -129,6 +129,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
             {post.locked &&
               <small className="ml-2 text-muted font-italic"><T i18nKey="locked">#</T></small>
             }
+            {post.stickied &&
+              <small className="ml-2 text-muted font-italic"><T i18nKey="stickied">#</T></small>
+            }
             {post.nsfw &&
               <small className="ml-2 text-muted font-italic"><T i18nKey="nsfw">#</T></small>
             }
@@ -202,6 +205,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
                     <li className="list-inline-item">
                       <span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{post.locked ? i18n.t('unlock') : i18n.t('lock')}</span>
                     </li>
+                    <li className="list-inline-item">
+                      <span class="pointer" onClick={linkEvent(this, this.handleModSticky)}>{post.stickied ? i18n.t('unsticky') : i18n.t('sticky')}</span>
+                    </li>
                   </>
                 }
                 {/* Mods can ban from community, and appoint as mods to community */}
@@ -459,6 +465,19 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     WebSocketService.Instance.editPost(form);
   }
 
+  handleModSticky(i: PostListing) {
+    let form: PostFormI = {
+      name: i.props.post.name,
+      community_id: i.props.post.community_id,
+      edit_id: i.props.post.id,
+      creator_id: i.props.post.creator_id,
+      nsfw: i.props.post.nsfw,
+      stickied: !i.props.post.stickied,
+      auth: null,
+    };
+    WebSocketService.Instance.editPost(form);
+  }
+
   handleModBanFromCommunityShow(i: PostListing) {
     i.state.showBanDialog = true;
     i.state.banType = BanType.Community;
index f2675eb30b19553b9707b48656857c000f91ac80..8c78d7d8990eaf277b7664542f6245009434ecb9 100644 (file)
@@ -77,6 +77,7 @@ export interface Post {
   removed: boolean;
   deleted: boolean;
   locked: boolean;
+  stickied: boolean;
   nsfw: boolean;
   banned: boolean;
   banned_from_community: boolean;
@@ -235,6 +236,7 @@ export interface GetModlogResponse {
   op: string;
   removed_posts: Array<ModRemovePost>,
   locked_posts: Array<ModLockPost>,
+  stickied_posts: Array<ModStickyPost>,
   removed_comments: Array<ModRemoveComment>,
   removed_communities: Array<ModRemoveCommunity>,
   banned_from_community: Array<ModBanFromCommunity>,
@@ -268,6 +270,18 @@ export interface ModLockPost {
   community_name: string,
 }
 
+export interface ModStickyPost {
+  id: number,
+  mod_user_id: number,
+  post_id: number,
+  stickied?: boolean,
+  when_: string,
+  mod_user_name: string,
+  post_name: string,
+  community_id: number,
+  community_name: string,
+}
+
 export interface ModRemoveComment {
   id: number,
   mod_user_id: number,
@@ -425,6 +439,7 @@ export interface PostForm {
   deleted?: boolean;
   nsfw: boolean;
   locked?: boolean;
+  stickied?: boolean;
   reason?: string;
   auth: string;
 }
index f89a98793763b401d226511bb79407ff408b382e..5ec036713909a6d473557e03ccbb15ff3c96a40e 100644 (file)
@@ -32,6 +32,8 @@ export const en = {
     view_source: 'view source',
     unlock: 'unlock',
     lock: 'lock',
+    sticky: 'sticky',
+    unsticky: 'unsticky',
     link: 'link',
     mod: 'mod',
     mods: 'mods',
@@ -47,6 +49,7 @@ export const en = {
     remove: 'remove',
     removed: 'removed',
     locked: 'locked',
+    stickied: 'stickied',
     reason: 'Reason',
     mark_as_read: 'mark as read',
     mark_as_unread: 'mark as unread',