]> Untitled Git - lemmy.git/commitdiff
Add support for Featured Posts (#2585)
authorAnon <makotech222@users.noreply.github.com>
Mon, 12 Dec 2022 11:17:10 +0000 (05:17 -0600)
committerGitHub <noreply@github.com>
Mon, 12 Dec 2022 11:17:10 +0000 (11:17 +0000)
* Add support for Featured Posts

* Fix rebase

* More fixes

30 files changed:
api_tests/package.json
api_tests/src/post.spec.ts
api_tests/src/shared.ts
api_tests/yarn.lock
crates/api/src/post/feature.rs [moved from crates/api/src/post/sticky.rs with 56% similarity]
crates/api/src/post/mod.rs
crates/api/src/site/mod_log.rs
crates/api_common/src/post.rs
crates/api_common/src/site.rs
crates/api_common/src/websocket/mod.rs
crates/apub/src/activities/create_or_update/post.rs
crates/apub/src/objects/post.rs
crates/apub/src/protocol/objects/page.rs
crates/db_schema/src/aggregates/structs.rs
crates/db_schema/src/impls/moderator.rs
crates/db_schema/src/impls/post.rs
crates/db_schema/src/lib.rs
crates/db_schema/src/schema.rs
crates/db_schema/src/source/moderator.rs
crates/db_schema/src/source/post.rs
crates/db_views/src/comment_view.rs
crates/db_views/src/post_report_view.rs
crates/db_views/src/post_view.rs
crates/db_views_moderator/src/lib.rs
crates/db_views_moderator/src/mod_feature_post_view.rs [moved from crates/db_views_moderator/src/mod_sticky_post_view.rs with 75% similarity]
crates/db_views_moderator/src/structs.rs
migrations/2022-11-20-032430_sticky_local/down.sql [new file with mode: 0644]
migrations/2022-11-20-032430_sticky_local/up.sql [new file with mode: 0644]
src/api_routes_http.rs
src/api_routes_websocket.rs

index 82d2f0eb7644ec88c0a0c51b3e86257af29f3454..20d919b284967c60e95c083d413c5ab80a5b8def 100644 (file)
@@ -20,7 +20,7 @@
     "eslint": "^8.25.0",
     "eslint-plugin-prettier": "^4.0.0",
     "jest": "^27.0.6",
-    "lemmy-js-client": "0.17.0-rc.48",
+    "lemmy-js-client": "0.17.0-rc.56",
     "node-fetch": "^2.6.1",
     "prettier": "^2.7.1",
     "reflect-metadata": "^0.1.13",
index d0e42c726ba52f3d26ac024e1c07bbba8d13c040..264b1cd7d457a2a419c6e278100f9dd102b1ade1 100644 (file)
@@ -11,7 +11,7 @@ import {
   setupLogins,
   createPost,
   editPost,
-  stickyPost,
+  featurePost,
   lockPost,
   resolvePost,
   likePost,
@@ -157,8 +157,8 @@ test("Sticky a post", async () => {
   let betaPost1 = (
     await resolvePost(beta, postRes.post_view.post)
   ).post.unwrap();
-  let stickiedPostRes = await stickyPost(beta, true, betaPost1.post);
-  expect(stickiedPostRes.post_view.post.stickied).toBe(true);
+  let stickiedPostRes = await featurePost(beta, true, betaPost1.post);
+  expect(stickiedPostRes.post_view.post.featured_community).toBe(true);
 
   // Make sure that post is stickied on beta
   let betaPost = (
@@ -166,11 +166,11 @@ test("Sticky a post", async () => {
   ).post.unwrap();
   expect(betaPost.community.local).toBe(true);
   expect(betaPost.creator.local).toBe(false);
-  expect(betaPost.post.stickied).toBe(true);
+  expect(betaPost.post.featured_community).toBe(true);
 
   // Unsticky a post
-  let unstickiedPost = await stickyPost(beta, false, betaPost1.post);
-  expect(unstickiedPost.post_view.post.stickied).toBe(false);
+  let unstickiedPost = await featurePost(beta, false, betaPost1.post);
+  expect(unstickiedPost.post_view.post.featured_community).toBe(false);
 
   // Make sure that post is unstickied on beta
   let betaPost2 = (
@@ -178,18 +178,18 @@ test("Sticky a post", async () => {
   ).post.unwrap();
   expect(betaPost2.community.local).toBe(true);
   expect(betaPost2.creator.local).toBe(false);
-  expect(betaPost2.post.stickied).toBe(false);
+  expect(betaPost2.post.featured_community).toBe(false);
 
   // Make sure that gamma cannot sticky the post on beta
   let gammaPost = (
     await resolvePost(gamma, postRes.post_view.post)
   ).post.unwrap();
-  let gammaTrySticky = await stickyPost(gamma, true, gammaPost.post);
+  let gammaTrySticky = await featurePost(gamma, true, gammaPost.post);
   let betaPost3 = (
     await resolvePost(beta, postRes.post_view.post)
   ).post.unwrap();
-  expect(gammaTrySticky.post_view.post.stickied).toBe(true);
-  expect(betaPost3.post.stickied).toBe(false);
+  expect(gammaTrySticky.post_view.post.featured_community).toBe(true);
+  expect(betaPost3.post.featured_community).toBe(false);
 });
 
 test("Lock a post", async () => {
index 9bf232065e0a71988e71d08d0d9c9a6505b24235..efce35f55e9478026491791ffa1bfe7104acd84b 100644 (file)
@@ -7,7 +7,6 @@ import {
   CreateComment,
   DeletePost,
   RemovePost,
-  StickyPost,
   LockPost,
   PostResponse,
   SearchResponse,
@@ -64,6 +63,8 @@ import {
   CommentSortType,
   GetComments,
   GetCommentsResponse,
+  FeaturePost,
+  PostFeatureType,
 } from "lemmy-js-client";
 
 export interface API {
@@ -180,14 +181,13 @@ export async function setupLogins() {
     rate_limit_search: Some(999),
     rate_limit_search_per_second: None,
     federation_enabled: None,
-    federation_strict_allowlist: None,
-    federation_http_fetch_retry_limit: None,
     federation_worker_count: None,
     captcha_enabled: None,
     captcha_difficulty: None,
     allowed_instances: None,
     blocked_instances: None,
     auth: "",
+    taglines: None,
   });
 
   // Set the blocks and auths for each
@@ -293,17 +293,18 @@ export async function removePost(
   return api.client.removePost(form);
 }
 
-export async function stickyPost(
+export async function featurePost(
   api: API,
-  stickied: boolean,
+  featured: boolean,
   post: Post
 ): Promise<PostResponse> {
-  let form = new StickyPost({
+  let form = new FeaturePost({
     post_id: post.id,
-    stickied,
+    featured,
+    feature_type: PostFeatureType.Community,
     auth: api.auth.unwrap(),
   });
-  return api.client.stickyPost(form);
+  return api.client.featurePost(form);
 }
 
 export async function lockPost(
index a6b1eef9778a50ed6382ddc675b25c8054f039a7..79d7942da7315654bc160d2bea7839a98733b46f 100644 (file)
@@ -2373,10 +2373,15 @@ kleur@^3.0.3:
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
   integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
 
-lemmy-js-client@0.17.0-rc.48:
-  version "0.17.0-rc.48"
-  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.48.tgz#6085812d4901b7d12b3fca237d8aced7f5210eac"
-  integrity sha512-Lz8Nzq/kczQtDj6STlbhxoEarFHtTCoWcWBabyPs6X6em/pfK/cnZqx1mMn7EaBSDUVQ+WL8UNFjQiqjhR4kww==
+lemmy-js-client@0.17.0-rc.56:
+  version "0.17.0-rc.56"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.56.tgz#2c7abba9b8195826eb36401e7c5c2cb75609bcf2"
+  integrity sha512-7MM5xV8H9fIr1TbM/4e9PFKJpwlD2t135pSiH92TFgdkTzOMf0mtLO2BWLAQ7Rq+XVoVgj/WSBR4BofJka8XRQ==
+  dependencies:
+    "@sniptt/monads" "^0.5.10"
+    class-transformer "^0.5.1"
+    node-fetch "2.6.6"
+    reflect-metadata "^0.1.13"
 
 leven@^3.1.0:
   version "3.1.0"
@@ -2511,6 +2516,13 @@ natural-compare@^1.4.0:
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
+node-fetch@2.6.6:
+  version "2.6.6"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
+  integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
+  dependencies:
+    whatwg-url "^5.0.0"
+
 node-fetch@^2.6.1:
   version "2.6.7"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
similarity index 56%
rename from crates/api/src/post/sticky.rs
rename to crates/api/src/post/feature.rs
index 3a8aee7ab99c464edb1df958ae5212719d10a351..16ba7bfb2bbcfb67ccb9a11172df25e929eb16ae 100644 (file)
@@ -2,26 +2,28 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   context::LemmyContext,
-  post::{PostResponse, StickyPost},
+  post::{FeaturePost, PostResponse},
   utils::{
     check_community_ban,
     check_community_deleted_or_removed,
     get_local_user_view_from_jwt,
+    is_admin,
     is_mod_or_admin,
   },
   websocket::{send::send_post_ws_message, UserOperation},
 };
 use lemmy_db_schema::{
   source::{
-    moderator::{ModStickyPost, ModStickyPostForm},
+    moderator::{ModFeaturePost, ModFeaturePostForm},
     post::{Post, PostUpdateForm},
   },
   traits::Crud,
+  PostFeatureType,
 };
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl Perform for StickyPost {
+impl Perform for FeaturePost {
   type Response = PostResponse;
 
   #[tracing::instrument(skip(context, websocket_id))]
@@ -30,7 +32,7 @@ impl Perform for StickyPost {
     context: &Data<LemmyContext>,
     websocket_id: Option<ConnectionId>,
   ) -> Result<PostResponse, LemmyError> {
-    let data: &StickyPost = self;
+    let data: &FeaturePost = self;
     let local_user_view =
       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
 
@@ -45,36 +47,44 @@ impl Perform for StickyPost {
     .await?;
     check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
 
-    // Verify that only the mods can sticky
-    is_mod_or_admin(
-      context.pool(),
-      local_user_view.person.id,
-      orig_post.community_id,
-    )
-    .await?;
+    if data.feature_type == PostFeatureType::Community {
+      // Verify that only the mods can feature in community
+      is_mod_or_admin(
+        context.pool(),
+        local_user_view.person.id,
+        orig_post.community_id,
+      )
+      .await?;
+    } else {
+      is_admin(&local_user_view)?;
+    }
 
     // Update the post
     let post_id = data.post_id;
-    let stickied = data.stickied;
-    Post::update(
-      context.pool(),
-      post_id,
-      &PostUpdateForm::builder().stickied(Some(stickied)).build(),
-    )
-    .await?;
+    let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community {
+      PostUpdateForm::builder()
+        .featured_community(Some(data.featured))
+        .build()
+    } else {
+      PostUpdateForm::builder()
+        .featured_local(Some(data.featured))
+        .build()
+    };
+    Post::update(context.pool(), post_id, &new_post).await?;
 
     // Mod tables
-    let form = ModStickyPostForm {
+    let form = ModFeaturePostForm {
       mod_person_id: local_user_view.person.id,
       post_id: data.post_id,
-      stickied: Some(stickied),
+      featured: data.featured,
+      is_featured_community: data.feature_type == PostFeatureType::Community,
     };
 
-    ModStickyPost::create(context.pool(), &form).await?;
+    ModFeaturePost::create(context.pool(), &form).await?;
 
     send_post_ws_message(
       data.post_id,
-      UserOperation::StickyPost,
+      UserOperation::FeaturePost,
       websocket_id,
       Some(local_user_view.person.id),
       context,
index 40cea54a25ea0440257d49bb7d97d37b8493004c..4d82513564e04ac414057a1aefc127f38f2bef31 100644 (file)
@@ -1,6 +1,6 @@
+mod feature;
 mod get_link_metadata;
 mod like;
 mod lock;
 mod mark_read;
 mod save;
-mod sticky;
index 5e1c71a701e2d4969b75aedfb4eb57d8a4aa5b57..ec3ee0f70b5d19d6decc13e50112787785963fe9 100644 (file)
@@ -19,12 +19,12 @@ use lemmy_db_views_moderator::structs::{
   ModAddView,
   ModBanFromCommunityView,
   ModBanView,
+  ModFeaturePostView,
   ModHideCommunityView,
   ModLockPostView,
   ModRemoveCommentView,
   ModRemoveCommunityView,
   ModRemovePostView,
-  ModStickyPostView,
   ModTransferCommunityView,
   ModlogListParams,
 };
@@ -91,8 +91,8 @@ impl Perform for GetModlog {
       _ => Default::default(),
     };
 
-    let stickied_posts = match type_ {
-      All | ModStickyPost => ModStickyPostView::list(context.pool(), params).await?,
+    let featured_posts = match type_ {
+      All | ModFeaturePost => ModFeaturePostView::list(context.pool(), params).await?,
       _ => Default::default(),
     };
 
@@ -181,7 +181,7 @@ impl Perform for GetModlog {
     Ok(GetModlogResponse {
       removed_posts,
       locked_posts,
-      stickied_posts,
+      featured_posts,
       removed_comments,
       removed_communities,
       banned_from_community,
index 4f486e0598da8ead2abc90eddd4122a72707a816..82a1d66499f037d82d998a8957a6363491a10219 100644 (file)
@@ -2,6 +2,7 @@ use crate::sensitive::Sensitive;
 use lemmy_db_schema::{
   newtypes::{CommentId, CommunityId, DbUrl, LanguageId, PostId, PostReportId},
   ListingType,
+  PostFeatureType,
   SortType,
 };
 use lemmy_db_views::structs::{PostReportView, PostView};
@@ -106,9 +107,10 @@ pub struct LockPost {
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
-pub struct StickyPost {
+pub struct FeaturePost {
   pub post_id: PostId,
-  pub stickied: bool,
+  pub featured: bool,
+  pub feature_type: PostFeatureType,
   pub auth: Sensitive<String>,
 }
 
index 94e0ce7e701fabdc7bf9cef337890e33fc7de3ae..c3f321ce704e606064001334a4446461b67381de 100644 (file)
@@ -31,12 +31,12 @@ use lemmy_db_views_moderator::structs::{
   ModAddView,
   ModBanFromCommunityView,
   ModBanView,
+  ModFeaturePostView,
   ModHideCommunityView,
   ModLockPostView,
   ModRemoveCommentView,
   ModRemoveCommunityView,
   ModRemovePostView,
-  ModStickyPostView,
   ModTransferCommunityView,
 };
 use serde::{Deserialize, Serialize};
@@ -93,7 +93,7 @@ pub struct GetModlog {
 pub struct GetModlogResponse {
   pub removed_posts: Vec<ModRemovePostView>,
   pub locked_posts: Vec<ModLockPostView>,
-  pub stickied_posts: Vec<ModStickyPostView>,
+  pub featured_posts: Vec<ModFeaturePostView>,
   pub removed_comments: Vec<ModRemoveCommentView>,
   pub removed_communities: Vec<ModRemoveCommunityView>,
   pub banned_from_community: Vec<ModBanFromCommunityView>,
index 8686b8e4f8289163fdf0814375195202cedaf9d5..2af6cff06e9a94bbaab72442e727ebeafaeb1afe 100644 (file)
@@ -38,7 +38,7 @@ pub enum UserOperation {
   ListCommentReports,
   CreatePostLike,
   LockPost,
-  StickyPost,
+  FeaturePost,
   MarkPostAsRead,
   SavePost,
   CreatePostReport,
index 92cc708774442b536332f468c71217c7eb67bdf3..69eae583c80f833d7d2d985bbeb546247d00a01c 100644 (file)
@@ -25,7 +25,7 @@ use activitypub_federation::{
 use activitystreams_kinds::public;
 use lemmy_api_common::{
   context::LemmyContext,
-  post::{CreatePost, EditPost, LockPost, PostResponse, StickyPost},
+  post::{CreatePost, EditPost, FeaturePost, LockPost, PostResponse},
   utils::get_local_user_view_from_jwt,
   websocket::{send::send_post_ws_message, UserOperationCrud},
 };
@@ -101,7 +101,7 @@ impl SendActivity for LockPost {
 }
 
 #[async_trait::async_trait(?Send)]
-impl SendActivity for StickyPost {
+impl SendActivity for FeaturePost {
   type Response = PostResponse;
 
   async fn send_activity(
@@ -205,9 +205,9 @@ impl ActivityHandler for CreateOrUpdatePage {
         // However, when fetching a remote post we generate a new create activity with the current
         // locked/stickied value, so this check may fail. So only check if its a local community,
         // because then we will definitely receive all create and update activities separately.
-        let is_stickied_or_locked =
+        let is_featured_or_locked =
           self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
-        if community.local && is_stickied_or_locked {
+        if community.local && is_featured_or_locked {
           return Err(LemmyError::from_message(
             "New post cannot be stickied or locked",
           ));
index ecad2e4649b1c1fa96ab430861573253fb8a9ae9..e15e1b2dc5367cd1aa6d9b425fb1684b0946e5cb 100644 (file)
@@ -32,7 +32,7 @@ use lemmy_db_schema::{
   source::{
     community::Community,
     local_site::LocalSite,
-    moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm},
+    moderator::{ModFeaturePost, ModFeaturePostForm, ModLockPost, ModLockPostForm},
     person::Person,
     post::{Post, PostInsertForm, PostUpdateForm},
   },
@@ -116,7 +116,7 @@ impl ApubObject for ApubPost {
       image: self.thumbnail_url.clone().map(ImageObject::new),
       comments_enabled: Some(!self.locked),
       sensitive: Some(self.nsfw),
-      stickied: Some(self.stickied),
+      stickied: Some(self.featured_community),
       language,
       published: Some(convert_datetime(self.published)),
       updated: self.updated.map(convert_datetime),
@@ -208,7 +208,6 @@ impl ApubObject for ApubPost {
         updated: page.updated.map(|u| u.naive_local()),
         deleted: Some(false),
         nsfw: page.sensitive,
-        stickied: page.stickied,
         embed_title,
         embed_description,
         embed_video_url,
@@ -216,6 +215,8 @@ impl ApubObject for ApubPost {
         ap_id: Some(page.id.clone().into()),
         local: Some(false),
         language_id,
+        featured_community: page.stickied,
+        featured_local: None,
       }
     } else {
       // if is mod action, only update locked/stickied fields, nothing else
@@ -225,7 +226,7 @@ impl ApubObject for ApubPost {
         .community_id(community.id)
         .ap_id(Some(page.id.clone().into()))
         .locked(page.comments_enabled.map(|e| !e))
-        .stickied(page.stickied)
+        .featured_community(page.stickied)
         .updated(page.updated.map(|u| u.naive_local()))
         .build()
     };
@@ -236,14 +237,15 @@ impl ApubObject for ApubPost {
 
     let post = Post::create(context.pool(), &form).await?;
 
-    // write mod log entries for sticky/lock
-    if Page::is_stickied_changed(&old_post, &page.stickied) {
-      let form = ModStickyPostForm {
+    // write mod log entries for feature/lock
+    if Page::is_featured_changed(&old_post, &page.stickied) {
+      let form = ModFeaturePostForm {
         mod_person_id: creator.id,
         post_id: post.id,
-        stickied: Some(post.stickied),
+        featured: post.featured_community,
+        is_featured_community: true,
       };
-      ModStickyPost::create(context.pool(), &form).await?;
+      ModFeaturePost::create(context.pool(), &form).await?;
     }
     if Page::is_locked_changed(&old_post, &page.comments_enabled) {
       let form = ModLockPostForm {
@@ -295,7 +297,7 @@ mod tests {
     assert!(post.body.is_some());
     assert_eq!(post.body.as_ref().unwrap().len(), 45);
     assert!(!post.locked);
-    assert!(post.stickied);
+    assert!(post.featured_community);
     assert_eq!(request_counter, 0);
 
     Post::delete(context.pool(), post.id).await.unwrap();
index 65df30e42e6c9b5c8374fc1538ea2ca39ea85a13..3aadb20c1a2ba5660e83c03f68c87568a25518e4 100644 (file)
@@ -137,18 +137,18 @@ impl Page {
       .dereference_local(context)
       .await;
 
-    let stickied_changed = Page::is_stickied_changed(&old_post, &self.stickied);
+    let featured_changed = Page::is_featured_changed(&old_post, &self.stickied);
     let locked_changed = Page::is_locked_changed(&old_post, &self.comments_enabled);
-    Ok(stickied_changed || locked_changed)
+    Ok(featured_changed || locked_changed)
   }
 
-  pub(crate) fn is_stickied_changed<E>(
+  pub(crate) fn is_featured_changed<E>(
     old_post: &Result<ApubPost, E>,
-    new_stickied: &Option<bool>,
+    new_featured_community: &Option<bool>,
   ) -> bool {
-    if let Some(new_stickied) = new_stickied {
+    if let Some(new_featured_community) = new_featured_community {
       if let Ok(old_post) = old_post {
-        return new_stickied != &old_post.stickied;
+        return new_featured_community != &old_post.featured_community;
       }
     }
 
index 08d5e9144099ef22e121df7489e3d11b78f5bb7b..d52b5bb419213e10288e80e000c44e17362d691f 100644 (file)
@@ -68,10 +68,11 @@ pub struct PostAggregates {
   pub score: i64,
   pub upvotes: i64,
   pub downvotes: i64,
-  pub stickied: bool,
   pub published: chrono::NaiveDateTime,
   pub newest_comment_time_necro: chrono::NaiveDateTime, // A newest comment time, limited to 2 days, to prevent necrobumping
   pub newest_comment_time: chrono::NaiveDateTime,
+  pub featured_community: bool,
+  pub featured_local: bool,
 }
 
 #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
index fc9b4803884b1878bfbf2fb0d1f285168ed6f2d9..23be96146dde355671cbb5c87ca698296a3c1f22 100644 (file)
@@ -16,6 +16,8 @@ use crate::{
     ModBanForm,
     ModBanFromCommunity,
     ModBanFromCommunityForm,
+    ModFeaturePost,
+    ModFeaturePostForm,
     ModHideCommunity,
     ModHideCommunityForm,
     ModLockPost,
@@ -26,8 +28,6 @@ use crate::{
     ModRemoveCommunityForm,
     ModRemovePost,
     ModRemovePostForm,
-    ModStickyPost,
-    ModStickyPostForm,
     ModTransferCommunity,
     ModTransferCommunityForm,
   },
@@ -98,29 +98,29 @@ impl Crud for ModLockPost {
 }
 
 #[async_trait]
-impl Crud for ModStickyPost {
-  type InsertForm = ModStickyPostForm;
-  type UpdateForm = ModStickyPostForm;
+impl Crud for ModFeaturePost {
+  type InsertForm = ModFeaturePostForm;
+  type UpdateForm = ModFeaturePostForm;
   type IdType = i32;
   async fn read(pool: &DbPool, from_id: i32) -> Result<Self, Error> {
-    use crate::schema::mod_sticky_post::dsl::mod_sticky_post;
+    use crate::schema::mod_feature_post::dsl::mod_feature_post;
     let conn = &mut get_conn(pool).await?;
-    mod_sticky_post.find(from_id).first::<Self>(conn).await
+    mod_feature_post.find(from_id).first::<Self>(conn).await
   }
 
-  async fn create(pool: &DbPool, form: &ModStickyPostForm) -> Result<Self, Error> {
-    use crate::schema::mod_sticky_post::dsl::mod_sticky_post;
+  async fn create(pool: &DbPool, form: &ModFeaturePostForm) -> Result<Self, Error> {
+    use crate::schema::mod_feature_post::dsl::mod_feature_post;
     let conn = &mut get_conn(pool).await?;
-    insert_into(mod_sticky_post)
+    insert_into(mod_feature_post)
       .values(form)
       .get_result::<Self>(conn)
       .await
   }
 
-  async fn update(pool: &DbPool, from_id: i32, form: &ModStickyPostForm) -> Result<Self, Error> {
-    use crate::schema::mod_sticky_post::dsl::mod_sticky_post;
+  async fn update(pool: &DbPool, from_id: i32, form: &ModFeaturePostForm) -> Result<Self, Error> {
+    use crate::schema::mod_feature_post::dsl::mod_feature_post;
     let conn = &mut get_conn(pool).await?;
-    diesel::update(mod_sticky_post.find(from_id))
+    diesel::update(mod_feature_post.find(from_id))
       .set(form)
       .get_result::<Self>(conn)
       .await
@@ -525,6 +525,8 @@ mod tests {
         ModBanForm,
         ModBanFromCommunity,
         ModBanFromCommunityForm,
+        ModFeaturePost,
+        ModFeaturePostForm,
         ModLockPost,
         ModLockPostForm,
         ModRemoveComment,
@@ -533,8 +535,6 @@ mod tests {
         ModRemoveCommunityForm,
         ModRemovePost,
         ModRemovePostForm,
-        ModStickyPost,
-        ModStickyPostForm,
       },
       person::{Person, PersonInsertForm},
       post::{Post, PostInsertForm},
@@ -637,25 +637,27 @@ mod tests {
       when_: inserted_mod_lock_post.when_,
     };
 
-    // sticky post
+    // feature post
 
-    let mod_sticky_post_form = ModStickyPostForm {
+    let mod_feature_post_form = ModFeaturePostForm {
       mod_person_id: inserted_mod.id,
       post_id: inserted_post.id,
-      stickied: None,
+      featured: false,
+      is_featured_community: true,
     };
-    let inserted_mod_sticky_post = ModStickyPost::create(pool, &mod_sticky_post_form)
+    let inserted_mod_feature_post = ModFeaturePost::create(pool, &mod_feature_post_form)
       .await
       .unwrap();
-    let read_mod_sticky_post = ModStickyPost::read(pool, inserted_mod_sticky_post.id)
+    let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id)
       .await
       .unwrap();
-    let expected_mod_sticky_post = ModStickyPost {
-      id: inserted_mod_sticky_post.id,
+    let expected_mod_feature_post = ModFeaturePost {
+      id: inserted_mod_feature_post.id,
       post_id: inserted_post.id,
       mod_person_id: inserted_mod.id,
-      stickied: Some(true),
-      when_: inserted_mod_sticky_post.when_,
+      featured: false,
+      is_featured_community: true,
+      when_: inserted_mod_feature_post.when_,
     };
 
     // comment
@@ -809,7 +811,7 @@ mod tests {
 
     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_feature_post, read_mod_feature_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);
index 407a83f5369b660d57a1a013abc0ed18181888df..2a5264402115473e1c45b3d18bac528343903725 100644 (file)
@@ -6,11 +6,11 @@ use crate::{
     community_id,
     creator_id,
     deleted,
+    featured_community,
     name,
     post,
     published,
     removed,
-    stickied,
     thumbnail_url,
     updated,
     url,
@@ -83,7 +83,7 @@ impl Post {
       .filter(deleted.eq(false))
       .filter(removed.eq(false))
       .then_order_by(published.desc())
-      .then_order_by(stickied.desc())
+      .then_order_by(featured_community.desc())
       .limit(FETCH_LIMIT_MAX)
       .load::<Self>(conn)
       .await
@@ -381,7 +381,6 @@ mod tests {
       published: inserted_post.published,
       removed: false,
       locked: false,
-      stickied: false,
       nsfw: false,
       deleted: false,
       updated: None,
@@ -392,6 +391,8 @@ mod tests {
       ap_id: inserted_post.ap_id.clone(),
       local: true,
       language_id: Default::default(),
+      featured_community: false,
+      featured_local: false,
     };
 
     // Post Like
index 4118b1eb471f4deb9ee7fc75ed359526207cbadc..b0e56393fc671354fd5d4d7dd1caef770b4f2bde 100644 (file)
@@ -82,7 +82,7 @@ pub enum ModlogActionType {
   All,
   ModRemovePost,
   ModLockPost,
-  ModStickyPost,
+  ModFeaturePost,
   ModRemoveComment,
   ModRemoveCommunity,
   ModBanFromCommunity,
@@ -96,3 +96,12 @@ pub enum ModlogActionType {
   AdminPurgePost,
   AdminPurgeComment,
 }
+
+#[derive(
+  EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq,
+)]
+pub enum PostFeatureType {
+  #[default]
+  Local,
+  Community,
+}
index f6e5aad760a13899c8faf0f660ad1fa9436f9a20..97def1ffa1ef0ce72c3370d8e849c8710fd83a7f 100644 (file)
@@ -273,12 +273,13 @@ table! {
 }
 
 table! {
-    mod_sticky_post (id) {
+    mod_feature_post (id) {
         id -> Int4,
         mod_person_id -> Int4,
         post_id -> Int4,
-        stickied -> Nullable<Bool>,
+        featured -> Bool,
         when_ -> Timestamp,
+        is_featured_community -> Bool,
     }
 }
 
@@ -371,7 +372,6 @@ table! {
         updated -> Nullable<Timestamp>,
         deleted -> Bool,
         nsfw -> Bool,
-        stickied -> Bool,
         embed_title -> Nullable<Text>,
         embed_description -> Nullable<Text>,
         embed_video_url -> Nullable<Text>,
@@ -379,6 +379,8 @@ table! {
         ap_id -> Varchar,
         local -> Bool,
         language_id -> Int4,
+        featured_community -> Bool,
+        featured_local -> Bool,
     }
 }
 
@@ -400,10 +402,11 @@ table! {
         score -> Int8,
         upvotes -> Int8,
         downvotes -> Int8,
-        stickied -> Bool,
         published -> Timestamp,
         newest_comment_time_necro -> Timestamp,
         newest_comment_time -> Timestamp,
+        featured_community -> Bool,
+        featured_local -> Bool,
     }
 }
 
@@ -771,8 +774,8 @@ joinable!(mod_remove_community -> community (community_id));
 joinable!(mod_remove_community -> person (mod_person_id));
 joinable!(mod_remove_post -> person (mod_person_id));
 joinable!(mod_remove_post -> post (post_id));
-joinable!(mod_sticky_post -> person (mod_person_id));
-joinable!(mod_sticky_post -> post (post_id));
+joinable!(mod_feature_post -> person (mod_person_id));
+joinable!(mod_feature_post -> post (post_id));
 joinable!(password_reset_request -> local_user (local_user_id));
 joinable!(person_aggregates -> person (person_id));
 joinable!(person_ban -> person (person_id));
@@ -848,7 +851,7 @@ allow_tables_to_appear_in_same_query!(
   mod_remove_comment,
   mod_remove_community,
   mod_remove_post,
-  mod_sticky_post,
+  mod_feature_post,
   mod_hide_community,
   password_reset_request,
   person,
index 000e34868de12d0a089e5a08eb8af9c6d92d6e51..5d9bda273d02b59af4da387b84fd6a570d5264f2 100644 (file)
@@ -9,12 +9,12 @@ use crate::schema::{
   mod_add_community,
   mod_ban,
   mod_ban_from_community,
+  mod_feature_post,
   mod_hide_community,
   mod_lock_post,
   mod_remove_comment,
   mod_remove_community,
   mod_remove_post,
-  mod_sticky_post,
   mod_transfer_community,
 };
 use serde::{Deserialize, Serialize};
@@ -61,21 +61,23 @@ pub struct ModLockPostForm {
 
 #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
 #[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
-#[cfg_attr(feature = "full", diesel(table_name = mod_sticky_post))]
-pub struct ModStickyPost {
+#[cfg_attr(feature = "full", diesel(table_name = mod_feature_post))]
+pub struct ModFeaturePost {
   pub id: i32,
   pub mod_person_id: PersonId,
   pub post_id: PostId,
-  pub stickied: Option<bool>,
+  pub featured: bool,
   pub when_: chrono::NaiveDateTime,
+  pub is_featured_community: bool,
 }
 
 #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
-#[cfg_attr(feature = "full", diesel(table_name = mod_sticky_post))]
-pub struct ModStickyPostForm {
+#[cfg_attr(feature = "full", diesel(table_name = mod_feature_post))]
+pub struct ModFeaturePostForm {
   pub mod_person_id: PersonId,
   pub post_id: PostId,
-  pub stickied: Option<bool>,
+  pub featured: bool,
+  pub is_featured_community: bool,
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
index df3116ef3907c599eca986521c7ce0a31556bca7..0b14b3f49407ddee2b49ad1c6a5349d2ed6d3ac8 100644 (file)
@@ -20,7 +20,6 @@ pub struct Post {
   pub updated: Option<chrono::NaiveDateTime>,
   pub deleted: bool,
   pub nsfw: bool,
-  pub stickied: bool,
   pub embed_title: Option<String>,
   pub embed_description: Option<String>,
   pub embed_video_url: Option<DbUrl>,
@@ -28,6 +27,8 @@ pub struct Post {
   pub ap_id: DbUrl,
   pub local: bool,
   pub language_id: LanguageId,
+  pub featured_community: bool,
+  pub featured_local: bool,
 }
 
 #[derive(Debug, Clone, TypedBuilder)]
@@ -49,7 +50,6 @@ pub struct PostInsertForm {
   pub updated: Option<chrono::NaiveDateTime>,
   pub published: Option<chrono::NaiveDateTime>,
   pub deleted: Option<bool>,
-  pub stickied: Option<bool>,
   pub embed_title: Option<String>,
   pub embed_description: Option<String>,
   pub embed_video_url: Option<DbUrl>,
@@ -57,6 +57,8 @@ pub struct PostInsertForm {
   pub ap_id: Option<DbUrl>,
   pub local: Option<bool>,
   pub language_id: Option<LanguageId>,
+  pub featured_community: Option<bool>,
+  pub featured_local: Option<bool>,
 }
 
 #[derive(Debug, Clone, TypedBuilder)]
@@ -73,7 +75,6 @@ pub struct PostUpdateForm {
   pub published: Option<chrono::NaiveDateTime>,
   pub updated: Option<Option<chrono::NaiveDateTime>>,
   pub deleted: Option<bool>,
-  pub stickied: Option<bool>,
   pub embed_title: Option<Option<String>>,
   pub embed_description: Option<Option<String>>,
   pub embed_video_url: Option<Option<DbUrl>>,
@@ -81,6 +82,8 @@ pub struct PostUpdateForm {
   pub ap_id: Option<DbUrl>,
   pub local: Option<bool>,
   pub language_id: Option<LanguageId>,
+  pub featured_community: Option<bool>,
+  pub featured_local: Option<bool>,
 }
 
 #[derive(PartialEq, Eq, Debug)]
index 051f198354ec9fb9dbd5378ce181ba09c796f27a..66daf831c212e6a44dc7d178dfb29857c0491130 100644 (file)
@@ -863,7 +863,6 @@ mod tests {
         removed: false,
         deleted: false,
         locked: false,
-        stickied: false,
         nsfw: false,
         embed_title: None,
         embed_description: None,
@@ -872,6 +871,8 @@ mod tests {
         ap_id: data.inserted_post.ap_id.clone(),
         local: true,
         language_id: Default::default(),
+        featured_community: false,
+        featured_local: false,
       },
       community: CommunitySafe {
         id: data.inserted_community.id,
index 40b90d67fb2cb09a776f4fab8f8e0bd22393fe45..523a19febbb778ecbabce7eed5d8ef63346d18a5 100644 (file)
@@ -469,10 +469,11 @@ mod tests {
         score: 0,
         upvotes: 0,
         downvotes: 0,
-        stickied: false,
         published: agg.published,
         newest_comment_time_necro: inserted_post.published,
         newest_comment_time: inserted_post.published,
+        featured_community: false,
+        featured_local: false,
       },
       resolver: None,
     };
index f5c8100fd186afd3dece252fcb1bce4dd581e80d..4a36b816fbe7dc55545f873f80b3c2d74321d913 100644 (file)
@@ -324,17 +324,16 @@ impl<'a> PostQuery<'a> {
         }
       }
     }
-
-    if let Some(community_id) = self.community_id {
+    if self.community_id.is_none() && self.community_actor_id.is_none() {
+      query = query.then_order_by(post_aggregates::featured_local.desc());
+    } else if let Some(community_id) = self.community_id {
       query = query
         .filter(post::community_id.eq(community_id))
-        .then_order_by(post_aggregates::stickied.desc());
-    }
-
-    if let Some(community_actor_id) = self.community_actor_id {
+        .then_order_by(post_aggregates::featured_community.desc());
+    } else if let Some(community_actor_id) = self.community_actor_id {
       query = query
         .filter(community::actor_id.eq(community_actor_id))
-        .then_order_by(post_aggregates::stickied.desc());
+        .then_order_by(post_aggregates::featured_community.desc());
     }
 
     if let Some(url_search) = self.url_search {
@@ -860,7 +859,6 @@ mod tests {
         removed: false,
         deleted: false,
         locked: false,
-        stickied: false,
         nsfw: false,
         embed_title: None,
         embed_description: None,
@@ -869,6 +867,8 @@ mod tests {
         ap_id: inserted_post.ap_id.clone(),
         local: true,
         language_id: LanguageId(47),
+        featured_community: false,
+        featured_local: false,
       },
       my_vote: None,
       unread_comments: 0,
@@ -919,10 +919,11 @@ mod tests {
         score: 0,
         upvotes: 0,
         downvotes: 0,
-        stickied: false,
         published: agg.published,
         newest_comment_time_necro: inserted_post.published,
         newest_comment_time: inserted_post.published,
+        featured_community: false,
+        featured_local: false,
       },
       subscribed: SubscribedType::NotSubscribed,
       read: false,
index 60d5c7877ef9a4943297768e9f686c5461fb9a24..d3e7efffde5e3dc4c6c5941b2532804eca1f9609 100644 (file)
@@ -15,6 +15,8 @@ pub mod mod_ban_from_community_view;
 #[cfg(feature = "full")]
 pub mod mod_ban_view;
 #[cfg(feature = "full")]
+pub mod mod_feature_post_view;
+#[cfg(feature = "full")]
 pub mod mod_hide_community_view;
 #[cfg(feature = "full")]
 pub mod mod_lock_post_view;
@@ -25,7 +27,5 @@ pub mod mod_remove_community_view;
 #[cfg(feature = "full")]
 pub mod mod_remove_post_view;
 #[cfg(feature = "full")]
-pub mod mod_sticky_post_view;
-#[cfg(feature = "full")]
 pub mod mod_transfer_community_view;
 pub mod structs;
similarity index 75%
rename from crates/db_views_moderator/src/mod_sticky_post_view.rs
rename to crates/db_views_moderator/src/mod_feature_post_view.rs
index 287362924a9d2524e31533a7740be5c7baaea3c0..852746de69ede1c4a1a3e6133aa49f05e5777d9a 100644 (file)
@@ -1,4 +1,4 @@
-use crate::structs::{ModStickyPostView, ModlogListParams};
+use crate::structs::{ModFeaturePostView, ModlogListParams};
 use diesel::{
   result::Error,
   BoolExpressionMethods,
@@ -11,10 +11,10 @@ use diesel::{
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   newtypes::PersonId,
-  schema::{community, mod_sticky_post, person, post},
+  schema::{community, mod_feature_post, person, post},
   source::{
     community::{Community, CommunitySafe},
-    moderator::ModStickyPost,
+    moderator::ModFeaturePost,
     person::{Person, PersonSafe},
     post::Post,
   },
@@ -22,9 +22,9 @@ use lemmy_db_schema::{
   utils::{get_conn, limit_and_offset, DbPool},
 };
 
-type ModStickyPostViewTuple = (ModStickyPost, Option<PersonSafe>, Post, CommunitySafe);
+type ModFeaturePostViewTuple = (ModFeaturePost, Option<PersonSafe>, Post, CommunitySafe);
 
-impl ModStickyPostView {
+impl ModFeaturePostView {
   pub async fn list(pool: &DbPool, params: ModlogListParams) -> Result<Vec<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
     let person_alias_1 = diesel::alias!(person as person1);
@@ -32,16 +32,16 @@ impl ModStickyPostView {
     let show_mod_names = !params.hide_modlog_names;
     let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>();
 
-    let admin_names_join = mod_sticky_post::mod_person_id
+    let admin_names_join = mod_feature_post::mod_person_id
       .eq(person::id)
       .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join)));
-    let mut query = mod_sticky_post::table
+    let mut query = mod_feature_post::table
       .left_join(person::table.on(admin_names_join))
       .inner_join(post::table)
       .inner_join(person_alias_1.on(post::creator_id.eq(person_alias_1.field(person::id))))
       .inner_join(community::table.on(post::community_id.eq(community::id)))
       .select((
-        mod_sticky_post::all_columns,
+        mod_feature_post::all_columns,
         Person::safe_columns_tuple().nullable(),
         post::all_columns,
         Community::safe_columns_tuple(),
@@ -53,7 +53,7 @@ impl ModStickyPostView {
     };
 
     if let Some(mod_person_id) = params.mod_person_id {
-      query = query.filter(mod_sticky_post::mod_person_id.eq(mod_person_id));
+      query = query.filter(mod_feature_post::mod_person_id.eq(mod_person_id));
     };
 
     if let Some(other_person_id) = params.other_person_id {
@@ -65,8 +65,8 @@ impl ModStickyPostView {
     let res = query
       .limit(limit)
       .offset(offset)
-      .order_by(mod_sticky_post::when_.desc())
-      .load::<ModStickyPostViewTuple>(conn)
+      .order_by(mod_feature_post::when_.desc())
+      .load::<ModFeaturePostViewTuple>(conn)
       .await?;
 
     let results = Self::from_tuple_to_vec(res);
@@ -74,13 +74,13 @@ impl ModStickyPostView {
   }
 }
 
-impl ViewToVec for ModStickyPostView {
-  type DbTuple = ModStickyPostViewTuple;
+impl ViewToVec for ModFeaturePostView {
+  type DbTuple = ModFeaturePostViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
       .into_iter()
       .map(|a| Self {
-        mod_sticky_post: a.0,
+        mod_feature_post: a.0,
         moderator: a.1,
         post: a.2,
         community: a.3,
index 863de0704f9a7e9e68f08c018ac4fc1157577fd3..85dc006b17a8b26dcc6d4ea6ef38530161e1c512 100644 (file)
@@ -12,12 +12,12 @@ use lemmy_db_schema::{
       ModAddCommunity,
       ModBan,
       ModBanFromCommunity,
+      ModFeaturePost,
       ModHideCommunity,
       ModLockPost,
       ModRemoveComment,
       ModRemoveCommunity,
       ModRemovePost,
-      ModStickyPost,
       ModTransferCommunity,
     },
     person::PersonSafe,
@@ -97,8 +97,8 @@ pub struct ModRemovePostView {
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ModStickyPostView {
-  pub mod_sticky_post: ModStickyPost,
+pub struct ModFeaturePostView {
+  pub mod_feature_post: ModFeaturePost,
   pub moderator: Option<PersonSafe>,
   pub post: Post,
   pub community: CommunitySafe,
diff --git a/migrations/2022-11-20-032430_sticky_local/down.sql b/migrations/2022-11-20-032430_sticky_local/down.sql
new file mode 100644 (file)
index 0000000..46a2b6a
--- /dev/null
@@ -0,0 +1,47 @@
+
+DROP TRIGGER IF EXISTS post_aggregates_featured_local ON post;
+DROP TRIGGER IF EXISTS post_aggregates_featured_community ON post;
+drop function post_aggregates_featured_community;
+drop function post_aggregates_featured_local;
+
+
+alter table post ADD stickied boolean NOT NULL DEFAULT false;
+Update post
+set stickied = featured_community;
+alter table post DROP COLUMN featured_community;
+alter table post DROP COLUMN featured_local;
+
+alter table post_aggregates ADD stickied boolean NOT NULL DEFAULT false;
+Update post_aggregates
+set stickied = featured_community;
+alter table post_aggregates DROP COLUMN featured_community;
+alter table post_aggregates DROP COLUMN featured_local;
+
+alter table mod_feature_post
+rename column featured TO stickied;
+
+alter table mod_feature_post
+DROP COLUMN is_featured_community;
+
+alter table mod_feature_post
+alter column stickied DROP NOT NULL;
+
+alter table mod_feature_post
+Rename To mod_sticky_post;
+
+create function post_aggregates_stickied()
+returns trigger language plpgsql
+as $$
+begin
+  update post_aggregates pa
+  set stickied = NEW.stickied
+  where pa.post_id = NEW.id;
+
+  return null;
+end $$;
+
+create trigger post_aggregates_stickied
+after update on post
+for each row
+when (OLD.stickied is distinct from NEW.stickied)
+execute procedure post_aggregates_stickied();
\ No newline at end of file
diff --git a/migrations/2022-11-20-032430_sticky_local/up.sql b/migrations/2022-11-20-032430_sticky_local/up.sql
new file mode 100644 (file)
index 0000000..202c4c4
--- /dev/null
@@ -0,0 +1,63 @@
+
+DROP TRIGGER IF EXISTS post_aggregates_stickied ON post;
+drop function 
+  post_aggregates_stickied;
+
+
+alter table post ADD featured_community boolean NOT NULL DEFAULT false;
+alter table post ADD featured_local boolean NOT NULL DEFAULT false;
+update post
+set featured_community = stickied;
+alter table post DROP COLUMN stickied;
+
+alter table post_aggregates ADD featured_community boolean NOT NULL DEFAULT false;
+alter table post_aggregates ADD featured_local boolean NOT NULL DEFAULT false;
+update post_aggregates
+set featured_community = stickied;
+alter table post_aggregates DROP COLUMN stickied;
+
+alter table mod_sticky_post
+rename column stickied TO featured;
+
+alter table mod_sticky_post
+alter column featured SET NOT NULL;
+
+alter table mod_sticky_post
+ADD is_featured_community boolean NOT NULL DEFAULT true;
+
+alter table mod_sticky_post
+Rename To mod_feature_post;
+
+create function post_aggregates_featured_community()
+returns trigger language plpgsql
+as $$
+begin
+  update post_aggregates pa
+  set featured_community = NEW.featured_community
+  where pa.post_id = NEW.id;
+  return null;
+end $$;
+
+create function post_aggregates_featured_local()
+returns trigger language plpgsql
+as $$
+begin
+  update post_aggregates pa
+  set featured_local = NEW.featured_local
+  where pa.post_id = NEW.id;
+  return null;
+end $$;
+
+CREATE TRIGGER post_aggregates_featured_community
+    AFTER UPDATE 
+    ON public.post
+    FOR EACH ROW
+    WHEN (old.featured_community IS DISTINCT FROM new.featured_community)
+    EXECUTE FUNCTION public.post_aggregates_featured_community();
+
+CREATE TRIGGER post_aggregates_featured_local
+    AFTER UPDATE 
+    ON public.post
+    FOR EACH ROW
+    WHEN (old.featured_local IS DISTINCT FROM new.featured_local)
+    EXECUTE FUNCTION public.post_aggregates_featured_local();
\ No newline at end of file
index 34083a48d19af22ea445770fd04378763ffc427b..ce7ce68bb93b8111c2f8eb2d0b46b9ba5ddd3d65 100644 (file)
@@ -59,6 +59,7 @@ use lemmy_api_common::{
     CreatePostReport,
     DeletePost,
     EditPost,
+    FeaturePost,
     GetPost,
     GetPosts,
     GetSiteMetadata,
@@ -68,7 +69,6 @@ use lemmy_api_common::{
     RemovePost,
     ResolvePostReport,
     SavePost,
-    StickyPost,
   },
   private_message::{
     CreatePrivateMessage,
@@ -183,7 +183,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
             web::post().to(route_post::<MarkPostAsRead>),
           )
           .route("/lock", web::post().to(route_post::<LockPost>))
-          .route("/sticky", web::post().to(route_post::<StickyPost>))
+          .route("/feature", web::post().to(route_post::<FeaturePost>))
           .route("/list", web::get().to(route_get_apub::<GetPosts>))
           .route("/like", web::post().to(route_post::<CreatePostLike>))
           .route("/save", web::put().to(route_post::<SavePost>))
index 7a8656005736b3e7672836d17749854813edd7fd..b258183827d341ea4593c50224fbfe57cda275aa 100644 (file)
@@ -60,6 +60,7 @@ use lemmy_api_common::{
     CreatePostReport,
     DeletePost,
     EditPost,
+    FeaturePost,
     GetPost,
     GetPosts,
     GetSiteMetadata,
@@ -69,7 +70,6 @@ use lemmy_api_common::{
     RemovePost,
     ResolvePostReport,
     SavePost,
-    StickyPost,
   },
   private_message::{
     CreatePrivateMessage,
@@ -560,7 +560,9 @@ pub async fn match_websocket_operation(
 
     // Post ops
     UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
-    UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
+    UserOperation::FeaturePost => {
+      do_websocket_operation::<FeaturePost>(context, id, op, data).await
+    }
     UserOperation::CreatePostLike => {
       do_websocket_operation::<CreatePostLike>(context, id, op, data).await
     }