Add support for Featured Posts (#2585)
* Add support for Featured Posts * Fix rebase * More fixes
This commit is contained in:
parent
0ecf256ce3
commit
9dfd819691
30 changed files with 319 additions and 156 deletions
|
@ -20,7 +20,7 @@
|
||||||
"eslint": "^8.25.0",
|
"eslint": "^8.25.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"jest": "^27.0.6",
|
"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",
|
"node-fetch": "^2.6.1",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
setupLogins,
|
setupLogins,
|
||||||
createPost,
|
createPost,
|
||||||
editPost,
|
editPost,
|
||||||
stickyPost,
|
featurePost,
|
||||||
lockPost,
|
lockPost,
|
||||||
resolvePost,
|
resolvePost,
|
||||||
likePost,
|
likePost,
|
||||||
|
@ -157,8 +157,8 @@ test("Sticky a post", async () => {
|
||||||
let betaPost1 = (
|
let betaPost1 = (
|
||||||
await resolvePost(beta, postRes.post_view.post)
|
await resolvePost(beta, postRes.post_view.post)
|
||||||
).post.unwrap();
|
).post.unwrap();
|
||||||
let stickiedPostRes = await stickyPost(beta, true, betaPost1.post);
|
let stickiedPostRes = await featurePost(beta, true, betaPost1.post);
|
||||||
expect(stickiedPostRes.post_view.post.stickied).toBe(true);
|
expect(stickiedPostRes.post_view.post.featured_community).toBe(true);
|
||||||
|
|
||||||
// Make sure that post is stickied on beta
|
// Make sure that post is stickied on beta
|
||||||
let betaPost = (
|
let betaPost = (
|
||||||
|
@ -166,11 +166,11 @@ test("Sticky a post", async () => {
|
||||||
).post.unwrap();
|
).post.unwrap();
|
||||||
expect(betaPost.community.local).toBe(true);
|
expect(betaPost.community.local).toBe(true);
|
||||||
expect(betaPost.creator.local).toBe(false);
|
expect(betaPost.creator.local).toBe(false);
|
||||||
expect(betaPost.post.stickied).toBe(true);
|
expect(betaPost.post.featured_community).toBe(true);
|
||||||
|
|
||||||
// Unsticky a post
|
// Unsticky a post
|
||||||
let unstickiedPost = await stickyPost(beta, false, betaPost1.post);
|
let unstickiedPost = await featurePost(beta, false, betaPost1.post);
|
||||||
expect(unstickiedPost.post_view.post.stickied).toBe(false);
|
expect(unstickiedPost.post_view.post.featured_community).toBe(false);
|
||||||
|
|
||||||
// Make sure that post is unstickied on beta
|
// Make sure that post is unstickied on beta
|
||||||
let betaPost2 = (
|
let betaPost2 = (
|
||||||
|
@ -178,18 +178,18 @@ test("Sticky a post", async () => {
|
||||||
).post.unwrap();
|
).post.unwrap();
|
||||||
expect(betaPost2.community.local).toBe(true);
|
expect(betaPost2.community.local).toBe(true);
|
||||||
expect(betaPost2.creator.local).toBe(false);
|
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
|
// Make sure that gamma cannot sticky the post on beta
|
||||||
let gammaPost = (
|
let gammaPost = (
|
||||||
await resolvePost(gamma, postRes.post_view.post)
|
await resolvePost(gamma, postRes.post_view.post)
|
||||||
).post.unwrap();
|
).post.unwrap();
|
||||||
let gammaTrySticky = await stickyPost(gamma, true, gammaPost.post);
|
let gammaTrySticky = await featurePost(gamma, true, gammaPost.post);
|
||||||
let betaPost3 = (
|
let betaPost3 = (
|
||||||
await resolvePost(beta, postRes.post_view.post)
|
await resolvePost(beta, postRes.post_view.post)
|
||||||
).post.unwrap();
|
).post.unwrap();
|
||||||
expect(gammaTrySticky.post_view.post.stickied).toBe(true);
|
expect(gammaTrySticky.post_view.post.featured_community).toBe(true);
|
||||||
expect(betaPost3.post.stickied).toBe(false);
|
expect(betaPost3.post.featured_community).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Lock a post", async () => {
|
test("Lock a post", async () => {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
CreateComment,
|
CreateComment,
|
||||||
DeletePost,
|
DeletePost,
|
||||||
RemovePost,
|
RemovePost,
|
||||||
StickyPost,
|
|
||||||
LockPost,
|
LockPost,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
|
@ -64,6 +63,8 @@ import {
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
GetComments,
|
GetComments,
|
||||||
GetCommentsResponse,
|
GetCommentsResponse,
|
||||||
|
FeaturePost,
|
||||||
|
PostFeatureType,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
|
|
||||||
export interface API {
|
export interface API {
|
||||||
|
@ -180,14 +181,13 @@ export async function setupLogins() {
|
||||||
rate_limit_search: Some(999),
|
rate_limit_search: Some(999),
|
||||||
rate_limit_search_per_second: None,
|
rate_limit_search_per_second: None,
|
||||||
federation_enabled: None,
|
federation_enabled: None,
|
||||||
federation_strict_allowlist: None,
|
|
||||||
federation_http_fetch_retry_limit: None,
|
|
||||||
federation_worker_count: None,
|
federation_worker_count: None,
|
||||||
captcha_enabled: None,
|
captcha_enabled: None,
|
||||||
captcha_difficulty: None,
|
captcha_difficulty: None,
|
||||||
allowed_instances: None,
|
allowed_instances: None,
|
||||||
blocked_instances: None,
|
blocked_instances: None,
|
||||||
auth: "",
|
auth: "",
|
||||||
|
taglines: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the blocks and auths for each
|
// Set the blocks and auths for each
|
||||||
|
@ -293,17 +293,18 @@ export async function removePost(
|
||||||
return api.client.removePost(form);
|
return api.client.removePost(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stickyPost(
|
export async function featurePost(
|
||||||
api: API,
|
api: API,
|
||||||
stickied: boolean,
|
featured: boolean,
|
||||||
post: Post
|
post: Post
|
||||||
): Promise<PostResponse> {
|
): Promise<PostResponse> {
|
||||||
let form = new StickyPost({
|
let form = new FeaturePost({
|
||||||
post_id: post.id,
|
post_id: post.id,
|
||||||
stickied,
|
featured,
|
||||||
|
feature_type: PostFeatureType.Community,
|
||||||
auth: api.auth.unwrap(),
|
auth: api.auth.unwrap(),
|
||||||
});
|
});
|
||||||
return api.client.stickyPost(form);
|
return api.client.featurePost(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function lockPost(
|
export async function lockPost(
|
||||||
|
|
|
@ -2373,10 +2373,15 @@ kleur@^3.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||||
|
|
||||||
lemmy-js-client@0.17.0-rc.48:
|
lemmy-js-client@0.17.0-rc.56:
|
||||||
version "0.17.0-rc.48"
|
version "0.17.0-rc.56"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.48.tgz#6085812d4901b7d12b3fca237d8aced7f5210eac"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.56.tgz#2c7abba9b8195826eb36401e7c5c2cb75609bcf2"
|
||||||
integrity sha512-Lz8Nzq/kczQtDj6STlbhxoEarFHtTCoWcWBabyPs6X6em/pfK/cnZqx1mMn7EaBSDUVQ+WL8UNFjQiqjhR4kww==
|
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:
|
leven@^3.1.0:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
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:
|
node-fetch@^2.6.1:
|
||||||
version "2.6.7"
|
version "2.6.7"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||||
|
|
|
@ -2,26 +2,28 @@ use crate::Perform;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{PostResponse, StickyPost},
|
post::{FeaturePost, PostResponse},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
|
is_admin,
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
},
|
},
|
||||||
websocket::{send::send_post_ws_message, UserOperation},
|
websocket::{send::send_post_ws_message, UserOperation},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
moderator::{ModStickyPost, ModStickyPostForm},
|
moderator::{ModFeaturePost, ModFeaturePostForm},
|
||||||
post::{Post, PostUpdateForm},
|
post::{Post, PostUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
|
PostFeatureType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for StickyPost {
|
impl Perform for FeaturePost {
|
||||||
type Response = PostResponse;
|
type Response = PostResponse;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context, websocket_id))]
|
#[tracing::instrument(skip(context, websocket_id))]
|
||||||
|
@ -30,7 +32,7 @@ impl Perform for StickyPost {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &StickyPost = self;
|
let data: &FeaturePost = self;
|
||||||
let local_user_view =
|
let local_user_view =
|
||||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
|
@ -45,36 +47,44 @@ impl Perform for StickyPost {
|
||||||
.await?;
|
.await?;
|
||||||
check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
|
check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
|
||||||
|
|
||||||
// Verify that only the mods can sticky
|
if data.feature_type == PostFeatureType::Community {
|
||||||
|
// Verify that only the mods can feature in community
|
||||||
is_mod_or_admin(
|
is_mod_or_admin(
|
||||||
context.pool(),
|
context.pool(),
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
orig_post.community_id,
|
orig_post.community_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
} else {
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the post
|
// Update the post
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let stickied = data.stickied;
|
let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community {
|
||||||
Post::update(
|
PostUpdateForm::builder()
|
||||||
context.pool(),
|
.featured_community(Some(data.featured))
|
||||||
post_id,
|
.build()
|
||||||
&PostUpdateForm::builder().stickied(Some(stickied)).build(),
|
} else {
|
||||||
)
|
PostUpdateForm::builder()
|
||||||
.await?;
|
.featured_local(Some(data.featured))
|
||||||
|
.build()
|
||||||
|
};
|
||||||
|
Post::update(context.pool(), post_id, &new_post).await?;
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModStickyPostForm {
|
let form = ModFeaturePostForm {
|
||||||
mod_person_id: local_user_view.person.id,
|
mod_person_id: local_user_view.person.id,
|
||||||
post_id: data.post_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(
|
send_post_ws_message(
|
||||||
data.post_id,
|
data.post_id,
|
||||||
UserOperation::StickyPost,
|
UserOperation::FeaturePost,
|
||||||
websocket_id,
|
websocket_id,
|
||||||
Some(local_user_view.person.id),
|
Some(local_user_view.person.id),
|
||||||
context,
|
context,
|
|
@ -1,6 +1,6 @@
|
||||||
|
mod feature;
|
||||||
mod get_link_metadata;
|
mod get_link_metadata;
|
||||||
mod like;
|
mod like;
|
||||||
mod lock;
|
mod lock;
|
||||||
mod mark_read;
|
mod mark_read;
|
||||||
mod save;
|
mod save;
|
||||||
mod sticky;
|
|
||||||
|
|
|
@ -19,12 +19,12 @@ use lemmy_db_views_moderator::structs::{
|
||||||
ModAddView,
|
ModAddView,
|
||||||
ModBanFromCommunityView,
|
ModBanFromCommunityView,
|
||||||
ModBanView,
|
ModBanView,
|
||||||
|
ModFeaturePostView,
|
||||||
ModHideCommunityView,
|
ModHideCommunityView,
|
||||||
ModLockPostView,
|
ModLockPostView,
|
||||||
ModRemoveCommentView,
|
ModRemoveCommentView,
|
||||||
ModRemoveCommunityView,
|
ModRemoveCommunityView,
|
||||||
ModRemovePostView,
|
ModRemovePostView,
|
||||||
ModStickyPostView,
|
|
||||||
ModTransferCommunityView,
|
ModTransferCommunityView,
|
||||||
ModlogListParams,
|
ModlogListParams,
|
||||||
};
|
};
|
||||||
|
@ -91,8 +91,8 @@ impl Perform for GetModlog {
|
||||||
_ => Default::default(),
|
_ => Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stickied_posts = match type_ {
|
let featured_posts = match type_ {
|
||||||
All | ModStickyPost => ModStickyPostView::list(context.pool(), params).await?,
|
All | ModFeaturePost => ModFeaturePostView::list(context.pool(), params).await?,
|
||||||
_ => Default::default(),
|
_ => Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ impl Perform for GetModlog {
|
||||||
Ok(GetModlogResponse {
|
Ok(GetModlogResponse {
|
||||||
removed_posts,
|
removed_posts,
|
||||||
locked_posts,
|
locked_posts,
|
||||||
stickied_posts,
|
featured_posts,
|
||||||
removed_comments,
|
removed_comments,
|
||||||
removed_communities,
|
removed_communities,
|
||||||
banned_from_community,
|
banned_from_community,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::sensitive::Sensitive;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{CommentId, CommunityId, DbUrl, LanguageId, PostId, PostReportId},
|
newtypes::{CommentId, CommunityId, DbUrl, LanguageId, PostId, PostReportId},
|
||||||
ListingType,
|
ListingType,
|
||||||
|
PostFeatureType,
|
||||||
SortType,
|
SortType,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{PostReportView, PostView};
|
use lemmy_db_views::structs::{PostReportView, PostView};
|
||||||
|
@ -106,9 +107,10 @@ pub struct LockPost {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
pub struct StickyPost {
|
pub struct FeaturePost {
|
||||||
pub post_id: PostId,
|
pub post_id: PostId,
|
||||||
pub stickied: bool,
|
pub featured: bool,
|
||||||
|
pub feature_type: PostFeatureType,
|
||||||
pub auth: Sensitive<String>,
|
pub auth: Sensitive<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@ use lemmy_db_views_moderator::structs::{
|
||||||
ModAddView,
|
ModAddView,
|
||||||
ModBanFromCommunityView,
|
ModBanFromCommunityView,
|
||||||
ModBanView,
|
ModBanView,
|
||||||
|
ModFeaturePostView,
|
||||||
ModHideCommunityView,
|
ModHideCommunityView,
|
||||||
ModLockPostView,
|
ModLockPostView,
|
||||||
ModRemoveCommentView,
|
ModRemoveCommentView,
|
||||||
ModRemoveCommunityView,
|
ModRemoveCommunityView,
|
||||||
ModRemovePostView,
|
ModRemovePostView,
|
||||||
ModStickyPostView,
|
|
||||||
ModTransferCommunityView,
|
ModTransferCommunityView,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -93,7 +93,7 @@ pub struct GetModlog {
|
||||||
pub struct GetModlogResponse {
|
pub struct GetModlogResponse {
|
||||||
pub removed_posts: Vec<ModRemovePostView>,
|
pub removed_posts: Vec<ModRemovePostView>,
|
||||||
pub locked_posts: Vec<ModLockPostView>,
|
pub locked_posts: Vec<ModLockPostView>,
|
||||||
pub stickied_posts: Vec<ModStickyPostView>,
|
pub featured_posts: Vec<ModFeaturePostView>,
|
||||||
pub removed_comments: Vec<ModRemoveCommentView>,
|
pub removed_comments: Vec<ModRemoveCommentView>,
|
||||||
pub removed_communities: Vec<ModRemoveCommunityView>,
|
pub removed_communities: Vec<ModRemoveCommunityView>,
|
||||||
pub banned_from_community: Vec<ModBanFromCommunityView>,
|
pub banned_from_community: Vec<ModBanFromCommunityView>,
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub enum UserOperation {
|
||||||
ListCommentReports,
|
ListCommentReports,
|
||||||
CreatePostLike,
|
CreatePostLike,
|
||||||
LockPost,
|
LockPost,
|
||||||
StickyPost,
|
FeaturePost,
|
||||||
MarkPostAsRead,
|
MarkPostAsRead,
|
||||||
SavePost,
|
SavePost,
|
||||||
CreatePostReport,
|
CreatePostReport,
|
||||||
|
|
|
@ -25,7 +25,7 @@ use activitypub_federation::{
|
||||||
use activitystreams_kinds::public;
|
use activitystreams_kinds::public;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePost, EditPost, LockPost, PostResponse, StickyPost},
|
post::{CreatePost, EditPost, FeaturePost, LockPost, PostResponse},
|
||||||
utils::get_local_user_view_from_jwt,
|
utils::get_local_user_view_from_jwt,
|
||||||
websocket::{send::send_post_ws_message, UserOperationCrud},
|
websocket::{send::send_post_ws_message, UserOperationCrud},
|
||||||
};
|
};
|
||||||
|
@ -101,7 +101,7 @@ impl SendActivity for LockPost {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl SendActivity for StickyPost {
|
impl SendActivity for FeaturePost {
|
||||||
type Response = PostResponse;
|
type Response = PostResponse;
|
||||||
|
|
||||||
async fn send_activity(
|
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
|
// 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,
|
// 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.
|
// 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);
|
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(
|
return Err(LemmyError::from_message(
|
||||||
"New post cannot be stickied or locked",
|
"New post cannot be stickied or locked",
|
||||||
));
|
));
|
||||||
|
|
|
@ -32,7 +32,7 @@ use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
community::Community,
|
community::Community,
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm},
|
moderator::{ModFeaturePost, ModFeaturePostForm, ModLockPost, ModLockPostForm},
|
||||||
person::Person,
|
person::Person,
|
||||||
post::{Post, PostInsertForm, PostUpdateForm},
|
post::{Post, PostInsertForm, PostUpdateForm},
|
||||||
},
|
},
|
||||||
|
@ -116,7 +116,7 @@ impl ApubObject for ApubPost {
|
||||||
image: self.thumbnail_url.clone().map(ImageObject::new),
|
image: self.thumbnail_url.clone().map(ImageObject::new),
|
||||||
comments_enabled: Some(!self.locked),
|
comments_enabled: Some(!self.locked),
|
||||||
sensitive: Some(self.nsfw),
|
sensitive: Some(self.nsfw),
|
||||||
stickied: Some(self.stickied),
|
stickied: Some(self.featured_community),
|
||||||
language,
|
language,
|
||||||
published: Some(convert_datetime(self.published)),
|
published: Some(convert_datetime(self.published)),
|
||||||
updated: self.updated.map(convert_datetime),
|
updated: self.updated.map(convert_datetime),
|
||||||
|
@ -208,7 +208,6 @@ impl ApubObject for ApubPost {
|
||||||
updated: page.updated.map(|u| u.naive_local()),
|
updated: page.updated.map(|u| u.naive_local()),
|
||||||
deleted: Some(false),
|
deleted: Some(false),
|
||||||
nsfw: page.sensitive,
|
nsfw: page.sensitive,
|
||||||
stickied: page.stickied,
|
|
||||||
embed_title,
|
embed_title,
|
||||||
embed_description,
|
embed_description,
|
||||||
embed_video_url,
|
embed_video_url,
|
||||||
|
@ -216,6 +215,8 @@ impl ApubObject for ApubPost {
|
||||||
ap_id: Some(page.id.clone().into()),
|
ap_id: Some(page.id.clone().into()),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
language_id,
|
language_id,
|
||||||
|
featured_community: page.stickied,
|
||||||
|
featured_local: None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if is mod action, only update locked/stickied fields, nothing else
|
// if is mod action, only update locked/stickied fields, nothing else
|
||||||
|
@ -225,7 +226,7 @@ impl ApubObject for ApubPost {
|
||||||
.community_id(community.id)
|
.community_id(community.id)
|
||||||
.ap_id(Some(page.id.clone().into()))
|
.ap_id(Some(page.id.clone().into()))
|
||||||
.locked(page.comments_enabled.map(|e| !e))
|
.locked(page.comments_enabled.map(|e| !e))
|
||||||
.stickied(page.stickied)
|
.featured_community(page.stickied)
|
||||||
.updated(page.updated.map(|u| u.naive_local()))
|
.updated(page.updated.map(|u| u.naive_local()))
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
|
@ -236,14 +237,15 @@ impl ApubObject for ApubPost {
|
||||||
|
|
||||||
let post = Post::create(context.pool(), &form).await?;
|
let post = Post::create(context.pool(), &form).await?;
|
||||||
|
|
||||||
// write mod log entries for sticky/lock
|
// write mod log entries for feature/lock
|
||||||
if Page::is_stickied_changed(&old_post, &page.stickied) {
|
if Page::is_featured_changed(&old_post, &page.stickied) {
|
||||||
let form = ModStickyPostForm {
|
let form = ModFeaturePostForm {
|
||||||
mod_person_id: creator.id,
|
mod_person_id: creator.id,
|
||||||
post_id: post.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) {
|
if Page::is_locked_changed(&old_post, &page.comments_enabled) {
|
||||||
let form = ModLockPostForm {
|
let form = ModLockPostForm {
|
||||||
|
@ -295,7 +297,7 @@ mod tests {
|
||||||
assert!(post.body.is_some());
|
assert!(post.body.is_some());
|
||||||
assert_eq!(post.body.as_ref().unwrap().len(), 45);
|
assert_eq!(post.body.as_ref().unwrap().len(), 45);
|
||||||
assert!(!post.locked);
|
assert!(!post.locked);
|
||||||
assert!(post.stickied);
|
assert!(post.featured_community);
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
|
||||||
Post::delete(context.pool(), post.id).await.unwrap();
|
Post::delete(context.pool(), post.id).await.unwrap();
|
||||||
|
|
|
@ -137,18 +137,18 @@ impl Page {
|
||||||
.dereference_local(context)
|
.dereference_local(context)
|
||||||
.await;
|
.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);
|
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>,
|
old_post: &Result<ApubPost, E>,
|
||||||
new_stickied: &Option<bool>,
|
new_featured_community: &Option<bool>,
|
||||||
) -> 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 {
|
if let Ok(old_post) = old_post {
|
||||||
return new_stickied != &old_post.stickied;
|
return new_featured_community != &old_post.featured_community;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,10 +68,11 @@ pub struct PostAggregates {
|
||||||
pub score: i64,
|
pub score: i64,
|
||||||
pub upvotes: i64,
|
pub upvotes: i64,
|
||||||
pub downvotes: i64,
|
pub downvotes: i64,
|
||||||
pub stickied: bool,
|
|
||||||
pub published: chrono::NaiveDateTime,
|
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_necro: chrono::NaiveDateTime, // A newest comment time, limited to 2 days, to prevent necrobumping
|
||||||
pub newest_comment_time: chrono::NaiveDateTime,
|
pub newest_comment_time: chrono::NaiveDateTime,
|
||||||
|
pub featured_community: bool,
|
||||||
|
pub featured_local: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
@ -16,6 +16,8 @@ use crate::{
|
||||||
ModBanForm,
|
ModBanForm,
|
||||||
ModBanFromCommunity,
|
ModBanFromCommunity,
|
||||||
ModBanFromCommunityForm,
|
ModBanFromCommunityForm,
|
||||||
|
ModFeaturePost,
|
||||||
|
ModFeaturePostForm,
|
||||||
ModHideCommunity,
|
ModHideCommunity,
|
||||||
ModHideCommunityForm,
|
ModHideCommunityForm,
|
||||||
ModLockPost,
|
ModLockPost,
|
||||||
|
@ -26,8 +28,6 @@ use crate::{
|
||||||
ModRemoveCommunityForm,
|
ModRemoveCommunityForm,
|
||||||
ModRemovePost,
|
ModRemovePost,
|
||||||
ModRemovePostForm,
|
ModRemovePostForm,
|
||||||
ModStickyPost,
|
|
||||||
ModStickyPostForm,
|
|
||||||
ModTransferCommunity,
|
ModTransferCommunity,
|
||||||
ModTransferCommunityForm,
|
ModTransferCommunityForm,
|
||||||
},
|
},
|
||||||
|
@ -98,29 +98,29 @@ impl Crud for ModLockPost {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for ModStickyPost {
|
impl Crud for ModFeaturePost {
|
||||||
type InsertForm = ModStickyPostForm;
|
type InsertForm = ModFeaturePostForm;
|
||||||
type UpdateForm = ModStickyPostForm;
|
type UpdateForm = ModFeaturePostForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &DbPool, from_id: i32) -> Result<Self, Error> {
|
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?;
|
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> {
|
async fn create(pool: &DbPool, form: &ModFeaturePostForm) -> 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?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(mod_sticky_post)
|
insert_into(mod_feature_post)
|
||||||
.values(form)
|
.values(form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(pool: &DbPool, from_id: i32, form: &ModStickyPostForm) -> Result<Self, Error> {
|
async fn update(pool: &DbPool, from_id: i32, form: &ModFeaturePostForm) -> 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?;
|
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)
|
.set(form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -525,6 +525,8 @@ mod tests {
|
||||||
ModBanForm,
|
ModBanForm,
|
||||||
ModBanFromCommunity,
|
ModBanFromCommunity,
|
||||||
ModBanFromCommunityForm,
|
ModBanFromCommunityForm,
|
||||||
|
ModFeaturePost,
|
||||||
|
ModFeaturePostForm,
|
||||||
ModLockPost,
|
ModLockPost,
|
||||||
ModLockPostForm,
|
ModLockPostForm,
|
||||||
ModRemoveComment,
|
ModRemoveComment,
|
||||||
|
@ -533,8 +535,6 @@ mod tests {
|
||||||
ModRemoveCommunityForm,
|
ModRemoveCommunityForm,
|
||||||
ModRemovePost,
|
ModRemovePost,
|
||||||
ModRemovePostForm,
|
ModRemovePostForm,
|
||||||
ModStickyPost,
|
|
||||||
ModStickyPostForm,
|
|
||||||
},
|
},
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
post::{Post, PostInsertForm},
|
post::{Post, PostInsertForm},
|
||||||
|
@ -637,25 +637,27 @@ mod tests {
|
||||||
when_: inserted_mod_lock_post.when_,
|
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,
|
mod_person_id: inserted_mod.id,
|
||||||
post_id: inserted_post.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
|
.await
|
||||||
.unwrap();
|
.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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected_mod_sticky_post = ModStickyPost {
|
let expected_mod_feature_post = ModFeaturePost {
|
||||||
id: inserted_mod_sticky_post.id,
|
id: inserted_mod_feature_post.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
mod_person_id: inserted_mod.id,
|
mod_person_id: inserted_mod.id,
|
||||||
stickied: Some(true),
|
featured: false,
|
||||||
when_: inserted_mod_sticky_post.when_,
|
is_featured_community: true,
|
||||||
|
when_: inserted_mod_feature_post.when_,
|
||||||
};
|
};
|
||||||
|
|
||||||
// comment
|
// comment
|
||||||
|
@ -809,7 +811,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(expected_mod_remove_post, read_mod_remove_post);
|
assert_eq!(expected_mod_remove_post, read_mod_remove_post);
|
||||||
assert_eq!(expected_mod_lock_post, read_mod_lock_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_comment, read_mod_remove_comment);
|
||||||
assert_eq!(expected_mod_remove_community, read_mod_remove_community);
|
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_from_community, read_mod_ban_from_community);
|
||||||
|
|
|
@ -6,11 +6,11 @@ use crate::{
|
||||||
community_id,
|
community_id,
|
||||||
creator_id,
|
creator_id,
|
||||||
deleted,
|
deleted,
|
||||||
|
featured_community,
|
||||||
name,
|
name,
|
||||||
post,
|
post,
|
||||||
published,
|
published,
|
||||||
removed,
|
removed,
|
||||||
stickied,
|
|
||||||
thumbnail_url,
|
thumbnail_url,
|
||||||
updated,
|
updated,
|
||||||
url,
|
url,
|
||||||
|
@ -83,7 +83,7 @@ impl Post {
|
||||||
.filter(deleted.eq(false))
|
.filter(deleted.eq(false))
|
||||||
.filter(removed.eq(false))
|
.filter(removed.eq(false))
|
||||||
.then_order_by(published.desc())
|
.then_order_by(published.desc())
|
||||||
.then_order_by(stickied.desc())
|
.then_order_by(featured_community.desc())
|
||||||
.limit(FETCH_LIMIT_MAX)
|
.limit(FETCH_LIMIT_MAX)
|
||||||
.load::<Self>(conn)
|
.load::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -381,7 +381,6 @@ mod tests {
|
||||||
published: inserted_post.published,
|
published: inserted_post.published,
|
||||||
removed: false,
|
removed: false,
|
||||||
locked: false,
|
locked: false,
|
||||||
stickied: false,
|
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
|
@ -392,6 +391,8 @@ mod tests {
|
||||||
ap_id: inserted_post.ap_id.clone(),
|
ap_id: inserted_post.ap_id.clone(),
|
||||||
local: true,
|
local: true,
|
||||||
language_id: Default::default(),
|
language_id: Default::default(),
|
||||||
|
featured_community: false,
|
||||||
|
featured_local: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Post Like
|
// Post Like
|
||||||
|
|
|
@ -82,7 +82,7 @@ pub enum ModlogActionType {
|
||||||
All,
|
All,
|
||||||
ModRemovePost,
|
ModRemovePost,
|
||||||
ModLockPost,
|
ModLockPost,
|
||||||
ModStickyPost,
|
ModFeaturePost,
|
||||||
ModRemoveComment,
|
ModRemoveComment,
|
||||||
ModRemoveCommunity,
|
ModRemoveCommunity,
|
||||||
ModBanFromCommunity,
|
ModBanFromCommunity,
|
||||||
|
@ -96,3 +96,12 @@ pub enum ModlogActionType {
|
||||||
AdminPurgePost,
|
AdminPurgePost,
|
||||||
AdminPurgeComment,
|
AdminPurgeComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq,
|
||||||
|
)]
|
||||||
|
pub enum PostFeatureType {
|
||||||
|
#[default]
|
||||||
|
Local,
|
||||||
|
Community,
|
||||||
|
}
|
||||||
|
|
|
@ -273,12 +273,13 @@ table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
mod_sticky_post (id) {
|
mod_feature_post (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
mod_person_id -> Int4,
|
mod_person_id -> Int4,
|
||||||
post_id -> Int4,
|
post_id -> Int4,
|
||||||
stickied -> Nullable<Bool>,
|
featured -> Bool,
|
||||||
when_ -> Timestamp,
|
when_ -> Timestamp,
|
||||||
|
is_featured_community -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +372,6 @@ table! {
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
deleted -> Bool,
|
deleted -> Bool,
|
||||||
nsfw -> Bool,
|
nsfw -> Bool,
|
||||||
stickied -> Bool,
|
|
||||||
embed_title -> Nullable<Text>,
|
embed_title -> Nullable<Text>,
|
||||||
embed_description -> Nullable<Text>,
|
embed_description -> Nullable<Text>,
|
||||||
embed_video_url -> Nullable<Text>,
|
embed_video_url -> Nullable<Text>,
|
||||||
|
@ -379,6 +379,8 @@ table! {
|
||||||
ap_id -> Varchar,
|
ap_id -> Varchar,
|
||||||
local -> Bool,
|
local -> Bool,
|
||||||
language_id -> Int4,
|
language_id -> Int4,
|
||||||
|
featured_community -> Bool,
|
||||||
|
featured_local -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,10 +402,11 @@ table! {
|
||||||
score -> Int8,
|
score -> Int8,
|
||||||
upvotes -> Int8,
|
upvotes -> Int8,
|
||||||
downvotes -> Int8,
|
downvotes -> Int8,
|
||||||
stickied -> Bool,
|
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
newest_comment_time_necro -> Timestamp,
|
newest_comment_time_necro -> Timestamp,
|
||||||
newest_comment_time -> 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_community -> person (mod_person_id));
|
||||||
joinable!(mod_remove_post -> person (mod_person_id));
|
joinable!(mod_remove_post -> person (mod_person_id));
|
||||||
joinable!(mod_remove_post -> post (post_id));
|
joinable!(mod_remove_post -> post (post_id));
|
||||||
joinable!(mod_sticky_post -> person (mod_person_id));
|
joinable!(mod_feature_post -> person (mod_person_id));
|
||||||
joinable!(mod_sticky_post -> post (post_id));
|
joinable!(mod_feature_post -> post (post_id));
|
||||||
joinable!(password_reset_request -> local_user (local_user_id));
|
joinable!(password_reset_request -> local_user (local_user_id));
|
||||||
joinable!(person_aggregates -> person (person_id));
|
joinable!(person_aggregates -> person (person_id));
|
||||||
joinable!(person_ban -> 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_comment,
|
||||||
mod_remove_community,
|
mod_remove_community,
|
||||||
mod_remove_post,
|
mod_remove_post,
|
||||||
mod_sticky_post,
|
mod_feature_post,
|
||||||
mod_hide_community,
|
mod_hide_community,
|
||||||
password_reset_request,
|
password_reset_request,
|
||||||
person,
|
person,
|
||||||
|
|
|
@ -9,12 +9,12 @@ use crate::schema::{
|
||||||
mod_add_community,
|
mod_add_community,
|
||||||
mod_ban,
|
mod_ban,
|
||||||
mod_ban_from_community,
|
mod_ban_from_community,
|
||||||
|
mod_feature_post,
|
||||||
mod_hide_community,
|
mod_hide_community,
|
||||||
mod_lock_post,
|
mod_lock_post,
|
||||||
mod_remove_comment,
|
mod_remove_comment,
|
||||||
mod_remove_community,
|
mod_remove_community,
|
||||||
mod_remove_post,
|
mod_remove_post,
|
||||||
mod_sticky_post,
|
|
||||||
mod_transfer_community,
|
mod_transfer_community,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -61,21 +61,23 @@ pub struct ModLockPostForm {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = mod_sticky_post))]
|
#[cfg_attr(feature = "full", diesel(table_name = mod_feature_post))]
|
||||||
pub struct ModStickyPost {
|
pub struct ModFeaturePost {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub mod_person_id: PersonId,
|
pub mod_person_id: PersonId,
|
||||||
pub post_id: PostId,
|
pub post_id: PostId,
|
||||||
pub stickied: Option<bool>,
|
pub featured: bool,
|
||||||
pub when_: chrono::NaiveDateTime,
|
pub when_: chrono::NaiveDateTime,
|
||||||
|
pub is_featured_community: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = mod_sticky_post))]
|
#[cfg_attr(feature = "full", diesel(table_name = mod_feature_post))]
|
||||||
pub struct ModStickyPostForm {
|
pub struct ModFeaturePostForm {
|
||||||
pub mod_person_id: PersonId,
|
pub mod_person_id: PersonId,
|
||||||
pub post_id: PostId,
|
pub post_id: PostId,
|
||||||
pub stickied: Option<bool>,
|
pub featured: bool,
|
||||||
|
pub is_featured_community: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -20,7 +20,6 @@ pub struct Post {
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
pub nsfw: bool,
|
pub nsfw: bool,
|
||||||
pub stickied: bool,
|
|
||||||
pub embed_title: Option<String>,
|
pub embed_title: Option<String>,
|
||||||
pub embed_description: Option<String>,
|
pub embed_description: Option<String>,
|
||||||
pub embed_video_url: Option<DbUrl>,
|
pub embed_video_url: Option<DbUrl>,
|
||||||
|
@ -28,6 +27,8 @@ pub struct Post {
|
||||||
pub ap_id: DbUrl,
|
pub ap_id: DbUrl,
|
||||||
pub local: bool,
|
pub local: bool,
|
||||||
pub language_id: LanguageId,
|
pub language_id: LanguageId,
|
||||||
|
pub featured_community: bool,
|
||||||
|
pub featured_local: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, TypedBuilder)]
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
@ -49,7 +50,6 @@ pub struct PostInsertForm {
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub published: Option<chrono::NaiveDateTime>,
|
pub published: Option<chrono::NaiveDateTime>,
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
pub stickied: Option<bool>,
|
|
||||||
pub embed_title: Option<String>,
|
pub embed_title: Option<String>,
|
||||||
pub embed_description: Option<String>,
|
pub embed_description: Option<String>,
|
||||||
pub embed_video_url: Option<DbUrl>,
|
pub embed_video_url: Option<DbUrl>,
|
||||||
|
@ -57,6 +57,8 @@ pub struct PostInsertForm {
|
||||||
pub ap_id: Option<DbUrl>,
|
pub ap_id: Option<DbUrl>,
|
||||||
pub local: Option<bool>,
|
pub local: Option<bool>,
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
|
pub featured_community: Option<bool>,
|
||||||
|
pub featured_local: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, TypedBuilder)]
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
@ -73,7 +75,6 @@ pub struct PostUpdateForm {
|
||||||
pub published: Option<chrono::NaiveDateTime>,
|
pub published: Option<chrono::NaiveDateTime>,
|
||||||
pub updated: Option<Option<chrono::NaiveDateTime>>,
|
pub updated: Option<Option<chrono::NaiveDateTime>>,
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
pub stickied: Option<bool>,
|
|
||||||
pub embed_title: Option<Option<String>>,
|
pub embed_title: Option<Option<String>>,
|
||||||
pub embed_description: Option<Option<String>>,
|
pub embed_description: Option<Option<String>>,
|
||||||
pub embed_video_url: Option<Option<DbUrl>>,
|
pub embed_video_url: Option<Option<DbUrl>>,
|
||||||
|
@ -81,6 +82,8 @@ pub struct PostUpdateForm {
|
||||||
pub ap_id: Option<DbUrl>,
|
pub ap_id: Option<DbUrl>,
|
||||||
pub local: Option<bool>,
|
pub local: Option<bool>,
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
|
pub featured_community: Option<bool>,
|
||||||
|
pub featured_local: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
|
|
@ -863,7 +863,6 @@ mod tests {
|
||||||
removed: false,
|
removed: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
locked: false,
|
locked: false,
|
||||||
stickied: false,
|
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
embed_title: None,
|
embed_title: None,
|
||||||
embed_description: None,
|
embed_description: None,
|
||||||
|
@ -872,6 +871,8 @@ mod tests {
|
||||||
ap_id: data.inserted_post.ap_id.clone(),
|
ap_id: data.inserted_post.ap_id.clone(),
|
||||||
local: true,
|
local: true,
|
||||||
language_id: Default::default(),
|
language_id: Default::default(),
|
||||||
|
featured_community: false,
|
||||||
|
featured_local: false,
|
||||||
},
|
},
|
||||||
community: CommunitySafe {
|
community: CommunitySafe {
|
||||||
id: data.inserted_community.id,
|
id: data.inserted_community.id,
|
||||||
|
|
|
@ -469,10 +469,11 @@ mod tests {
|
||||||
score: 0,
|
score: 0,
|
||||||
upvotes: 0,
|
upvotes: 0,
|
||||||
downvotes: 0,
|
downvotes: 0,
|
||||||
stickied: false,
|
|
||||||
published: agg.published,
|
published: agg.published,
|
||||||
newest_comment_time_necro: inserted_post.published,
|
newest_comment_time_necro: inserted_post.published,
|
||||||
newest_comment_time: inserted_post.published,
|
newest_comment_time: inserted_post.published,
|
||||||
|
featured_community: false,
|
||||||
|
featured_local: false,
|
||||||
},
|
},
|
||||||
resolver: None,
|
resolver: None,
|
||||||
};
|
};
|
||||||
|
|
|
@ -324,17 +324,16 @@ impl<'a> PostQuery<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.community_id.is_none() && self.community_actor_id.is_none() {
|
||||||
if let Some(community_id) = self.community_id {
|
query = query.then_order_by(post_aggregates::featured_local.desc());
|
||||||
|
} else if let Some(community_id) = self.community_id {
|
||||||
query = query
|
query = query
|
||||||
.filter(post::community_id.eq(community_id))
|
.filter(post::community_id.eq(community_id))
|
||||||
.then_order_by(post_aggregates::stickied.desc());
|
.then_order_by(post_aggregates::featured_community.desc());
|
||||||
}
|
} else if let Some(community_actor_id) = self.community_actor_id {
|
||||||
|
|
||||||
if let Some(community_actor_id) = self.community_actor_id {
|
|
||||||
query = query
|
query = query
|
||||||
.filter(community::actor_id.eq(community_actor_id))
|
.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 {
|
if let Some(url_search) = self.url_search {
|
||||||
|
@ -860,7 +859,6 @@ mod tests {
|
||||||
removed: false,
|
removed: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
locked: false,
|
locked: false,
|
||||||
stickied: false,
|
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
embed_title: None,
|
embed_title: None,
|
||||||
embed_description: None,
|
embed_description: None,
|
||||||
|
@ -869,6 +867,8 @@ mod tests {
|
||||||
ap_id: inserted_post.ap_id.clone(),
|
ap_id: inserted_post.ap_id.clone(),
|
||||||
local: true,
|
local: true,
|
||||||
language_id: LanguageId(47),
|
language_id: LanguageId(47),
|
||||||
|
featured_community: false,
|
||||||
|
featured_local: false,
|
||||||
},
|
},
|
||||||
my_vote: None,
|
my_vote: None,
|
||||||
unread_comments: 0,
|
unread_comments: 0,
|
||||||
|
@ -919,10 +919,11 @@ mod tests {
|
||||||
score: 0,
|
score: 0,
|
||||||
upvotes: 0,
|
upvotes: 0,
|
||||||
downvotes: 0,
|
downvotes: 0,
|
||||||
stickied: false,
|
|
||||||
published: agg.published,
|
published: agg.published,
|
||||||
newest_comment_time_necro: inserted_post.published,
|
newest_comment_time_necro: inserted_post.published,
|
||||||
newest_comment_time: inserted_post.published,
|
newest_comment_time: inserted_post.published,
|
||||||
|
featured_community: false,
|
||||||
|
featured_local: false,
|
||||||
},
|
},
|
||||||
subscribed: SubscribedType::NotSubscribed,
|
subscribed: SubscribedType::NotSubscribed,
|
||||||
read: false,
|
read: false,
|
||||||
|
|
|
@ -15,6 +15,8 @@ pub mod mod_ban_from_community_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod mod_ban_view;
|
pub mod mod_ban_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
pub mod mod_feature_post_view;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
pub mod mod_hide_community_view;
|
pub mod mod_hide_community_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod mod_lock_post_view;
|
pub mod mod_lock_post_view;
|
||||||
|
@ -25,7 +27,5 @@ pub mod mod_remove_community_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod mod_remove_post_view;
|
pub mod mod_remove_post_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod mod_sticky_post_view;
|
|
||||||
#[cfg(feature = "full")]
|
|
||||||
pub mod mod_transfer_community_view;
|
pub mod mod_transfer_community_view;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::structs::{ModStickyPostView, ModlogListParams};
|
use crate::structs::{ModFeaturePostView, ModlogListParams};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
result::Error,
|
result::Error,
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
|
@ -11,10 +11,10 @@ use diesel::{
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::PersonId,
|
newtypes::PersonId,
|
||||||
schema::{community, mod_sticky_post, person, post},
|
schema::{community, mod_feature_post, person, post},
|
||||||
source::{
|
source::{
|
||||||
community::{Community, CommunitySafe},
|
community::{Community, CommunitySafe},
|
||||||
moderator::ModStickyPost,
|
moderator::ModFeaturePost,
|
||||||
person::{Person, PersonSafe},
|
person::{Person, PersonSafe},
|
||||||
post::Post,
|
post::Post,
|
||||||
},
|
},
|
||||||
|
@ -22,9 +22,9 @@ use lemmy_db_schema::{
|
||||||
utils::{get_conn, limit_and_offset, DbPool},
|
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> {
|
pub async fn list(pool: &DbPool, params: ModlogListParams) -> Result<Vec<Self>, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
let person_alias_1 = diesel::alias!(person as person1);
|
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 = !params.hide_modlog_names;
|
||||||
let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>();
|
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)
|
.eq(person::id)
|
||||||
.and(show_mod_names_expr.or(person::id.eq(admin_person_id_join)));
|
.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))
|
.left_join(person::table.on(admin_names_join))
|
||||||
.inner_join(post::table)
|
.inner_join(post::table)
|
||||||
.inner_join(person_alias_1.on(post::creator_id.eq(person_alias_1.field(person::id))))
|
.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)))
|
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||||
.select((
|
.select((
|
||||||
mod_sticky_post::all_columns,
|
mod_feature_post::all_columns,
|
||||||
Person::safe_columns_tuple().nullable(),
|
Person::safe_columns_tuple().nullable(),
|
||||||
post::all_columns,
|
post::all_columns,
|
||||||
Community::safe_columns_tuple(),
|
Community::safe_columns_tuple(),
|
||||||
|
@ -53,7 +53,7 @@ impl ModStickyPostView {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mod_person_id) = params.mod_person_id {
|
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 {
|
if let Some(other_person_id) = params.other_person_id {
|
||||||
|
@ -65,8 +65,8 @@ impl ModStickyPostView {
|
||||||
let res = query
|
let res = query
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.order_by(mod_sticky_post::when_.desc())
|
.order_by(mod_feature_post::when_.desc())
|
||||||
.load::<ModStickyPostViewTuple>(conn)
|
.load::<ModFeaturePostViewTuple>(conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let results = Self::from_tuple_to_vec(res);
|
let results = Self::from_tuple_to_vec(res);
|
||||||
|
@ -74,13 +74,13 @@ impl ModStickyPostView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewToVec for ModStickyPostView {
|
impl ViewToVec for ModFeaturePostView {
|
||||||
type DbTuple = ModStickyPostViewTuple;
|
type DbTuple = ModFeaturePostViewTuple;
|
||||||
fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
|
fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
|
||||||
items
|
items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| Self {
|
.map(|a| Self {
|
||||||
mod_sticky_post: a.0,
|
mod_feature_post: a.0,
|
||||||
moderator: a.1,
|
moderator: a.1,
|
||||||
post: a.2,
|
post: a.2,
|
||||||
community: a.3,
|
community: a.3,
|
|
@ -12,12 +12,12 @@ use lemmy_db_schema::{
|
||||||
ModAddCommunity,
|
ModAddCommunity,
|
||||||
ModBan,
|
ModBan,
|
||||||
ModBanFromCommunity,
|
ModBanFromCommunity,
|
||||||
|
ModFeaturePost,
|
||||||
ModHideCommunity,
|
ModHideCommunity,
|
||||||
ModLockPost,
|
ModLockPost,
|
||||||
ModRemoveComment,
|
ModRemoveComment,
|
||||||
ModRemoveCommunity,
|
ModRemoveCommunity,
|
||||||
ModRemovePost,
|
ModRemovePost,
|
||||||
ModStickyPost,
|
|
||||||
ModTransferCommunity,
|
ModTransferCommunity,
|
||||||
},
|
},
|
||||||
person::PersonSafe,
|
person::PersonSafe,
|
||||||
|
@ -97,8 +97,8 @@ pub struct ModRemovePostView {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ModStickyPostView {
|
pub struct ModFeaturePostView {
|
||||||
pub mod_sticky_post: ModStickyPost,
|
pub mod_feature_post: ModFeaturePost,
|
||||||
pub moderator: Option<PersonSafe>,
|
pub moderator: Option<PersonSafe>,
|
||||||
pub post: Post,
|
pub post: Post,
|
||||||
pub community: CommunitySafe,
|
pub community: CommunitySafe,
|
||||||
|
|
47
migrations/2022-11-20-032430_sticky_local/down.sql
Normal file
47
migrations/2022-11-20-032430_sticky_local/down.sql
Normal file
|
@ -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();
|
63
migrations/2022-11-20-032430_sticky_local/up.sql
Normal file
63
migrations/2022-11-20-032430_sticky_local/up.sql
Normal file
|
@ -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();
|
|
@ -59,6 +59,7 @@ use lemmy_api_common::{
|
||||||
CreatePostReport,
|
CreatePostReport,
|
||||||
DeletePost,
|
DeletePost,
|
||||||
EditPost,
|
EditPost,
|
||||||
|
FeaturePost,
|
||||||
GetPost,
|
GetPost,
|
||||||
GetPosts,
|
GetPosts,
|
||||||
GetSiteMetadata,
|
GetSiteMetadata,
|
||||||
|
@ -68,7 +69,6 @@ use lemmy_api_common::{
|
||||||
RemovePost,
|
RemovePost,
|
||||||
ResolvePostReport,
|
ResolvePostReport,
|
||||||
SavePost,
|
SavePost,
|
||||||
StickyPost,
|
|
||||||
},
|
},
|
||||||
private_message::{
|
private_message::{
|
||||||
CreatePrivateMessage,
|
CreatePrivateMessage,
|
||||||
|
@ -183,7 +183,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
web::post().to(route_post::<MarkPostAsRead>),
|
web::post().to(route_post::<MarkPostAsRead>),
|
||||||
)
|
)
|
||||||
.route("/lock", web::post().to(route_post::<LockPost>))
|
.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("/list", web::get().to(route_get_apub::<GetPosts>))
|
||||||
.route("/like", web::post().to(route_post::<CreatePostLike>))
|
.route("/like", web::post().to(route_post::<CreatePostLike>))
|
||||||
.route("/save", web::put().to(route_post::<SavePost>))
|
.route("/save", web::put().to(route_post::<SavePost>))
|
||||||
|
|
|
@ -60,6 +60,7 @@ use lemmy_api_common::{
|
||||||
CreatePostReport,
|
CreatePostReport,
|
||||||
DeletePost,
|
DeletePost,
|
||||||
EditPost,
|
EditPost,
|
||||||
|
FeaturePost,
|
||||||
GetPost,
|
GetPost,
|
||||||
GetPosts,
|
GetPosts,
|
||||||
GetSiteMetadata,
|
GetSiteMetadata,
|
||||||
|
@ -69,7 +70,6 @@ use lemmy_api_common::{
|
||||||
RemovePost,
|
RemovePost,
|
||||||
ResolvePostReport,
|
ResolvePostReport,
|
||||||
SavePost,
|
SavePost,
|
||||||
StickyPost,
|
|
||||||
},
|
},
|
||||||
private_message::{
|
private_message::{
|
||||||
CreatePrivateMessage,
|
CreatePrivateMessage,
|
||||||
|
@ -560,7 +560,9 @@ pub async fn match_websocket_operation(
|
||||||
|
|
||||||
// Post ops
|
// Post ops
|
||||||
UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
|
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 => {
|
UserOperation::CreatePostLike => {
|
||||||
do_websocket_operation::<CreatePostLike>(context, id, op, data).await
|
do_websocket_operation::<CreatePostLike>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue