]> Untitled Git - lemmy.git/blob - server/src/db/post_view.rs
Adding emoji support.
[lemmy.git] / server / src / db / post_view.rs
1 use super::*;
2
3 #[derive(EnumString,ToString,Debug, Serialize, Deserialize)]
4 pub enum PostListingType {
5   All, Subscribed, Community
6 }
7
8 // The faked schema since diesel doesn't do views
9 table! {
10   post_view (id) {
11     id -> Int4,
12     name -> Varchar,
13     url -> Nullable<Text>,
14     body -> Nullable<Text>,
15     creator_id -> Int4,
16     community_id -> Int4,
17     removed -> Bool,
18     locked -> Bool,
19     published -> Timestamp,
20     updated -> Nullable<Timestamp>,
21     deleted -> Bool,
22     nsfw -> Bool,
23     creator_name -> Varchar,
24     community_name -> Varchar,
25     community_removed -> Bool,
26     community_deleted -> Bool,
27     community_nsfw -> Bool,
28     number_of_comments -> BigInt,
29     score -> BigInt,
30     upvotes -> BigInt,
31     downvotes -> BigInt,
32     hot_rank -> Int4,
33     user_id -> Nullable<Int4>,
34     my_vote -> Nullable<Int4>,
35     subscribed -> Nullable<Bool>,
36     read -> Nullable<Bool>,
37     saved -> Nullable<Bool>,
38   }
39 }
40
41
42 #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
43 #[table_name="post_view"]
44 pub struct PostView {
45   pub id: i32,
46   pub name: String,
47   pub url: Option<String>,
48   pub body: Option<String>,
49   pub creator_id: i32,
50   pub community_id: i32,
51   pub removed: bool,
52   pub locked: bool,
53   pub published: chrono::NaiveDateTime,
54   pub updated: Option<chrono::NaiveDateTime>,
55   pub deleted: bool,
56   pub nsfw: bool,
57   pub creator_name: String,
58   pub community_name: String,
59   pub community_removed: bool,
60   pub community_deleted: bool,
61   pub community_nsfw: bool,
62   pub number_of_comments: i64,
63   pub score: i64,
64   pub upvotes: i64,
65   pub downvotes: i64,
66   pub hot_rank: i32,
67   pub user_id: Option<i32>,
68   pub my_vote: Option<i32>,
69   pub subscribed: Option<bool>,
70   pub read: Option<bool>,
71   pub saved: Option<bool>,
72 }
73
74 impl PostView {
75   pub fn list(
76     conn: &PgConnection, 
77     type_: PostListingType, 
78     sort: &SortType, 
79     for_community_id: Option<i32>, 
80     for_creator_id: Option<i32>, 
81     search_term: Option<String>,
82     url_search: Option<String>,
83     my_user_id: Option<i32>, 
84     show_nsfw: bool,
85     saved_only: bool,
86     unread_only: bool,
87     page: Option<i64>,
88     limit: Option<i64>,
89     ) -> Result<Vec<Self>, Error> {
90     use super::post_view::post_view::dsl::*;
91
92     let (limit, offset) = limit_and_offset(page, limit);
93
94     let mut query = post_view.into_boxed();
95
96     if let Some(for_community_id) = for_community_id {
97       query = query.filter(community_id.eq(for_community_id));
98     };
99
100     if let Some(for_creator_id) = for_creator_id {
101       query = query.filter(creator_id.eq(for_creator_id));
102     };
103
104     if let Some(search_term) = search_term {
105       query = query.filter(name.ilike(fuzzy_search(&search_term)));
106     };
107
108     if let Some(url_search) = url_search {
109       query = query.filter(url.eq(url_search));
110     };
111
112     // TODO these are wrong, bc they'll only show saved for your logged in user, not theirs
113     if saved_only {
114       query = query.filter(saved.eq(true));
115     };
116
117     if unread_only {
118       query = query.filter(read.eq(false));
119     };
120
121     match type_ {
122       PostListingType::Subscribed  => {
123         query = query.filter(subscribed.eq(true));
124       },
125       _ => {}
126     };
127
128     // The view lets you pass a null user_id, if you're not logged in
129     if let Some(my_user_id) = my_user_id {
130       query = query.filter(user_id.eq(my_user_id));
131     } else {
132       query = query.filter(user_id.is_null());
133     }
134
135     if !show_nsfw {
136       query = query
137         .filter(nsfw.eq(false))
138         .filter(community_nsfw.eq(false));
139     };
140
141     query = match sort {
142       SortType::Hot => query.order_by(hot_rank.desc())
143         .then_order_by(published.desc()),
144       SortType::New => query.order_by(published.desc()),
145       SortType::TopAll => query.order_by(score.desc()),
146       SortType::TopYear => query
147         .filter(published.gt(now - 1.years()))
148         .order_by(score.desc()),
149         SortType::TopMonth => query
150           .filter(published.gt(now - 1.months()))
151           .order_by(score.desc()),
152           SortType::TopWeek => query
153             .filter(published.gt(now - 1.weeks()))
154             .order_by(score.desc()),
155             SortType::TopDay => query
156               .filter(published.gt(now - 1.days()))
157               .order_by(score.desc())
158     };
159
160     query = query
161       .limit(limit)
162       .offset(offset)
163       .filter(removed.eq(false))
164       .filter(deleted.eq(false))
165       .filter(community_removed.eq(false))
166       .filter(community_deleted.eq(false));
167
168     query.load::<Self>(conn) 
169   }
170
171
172   pub fn read(conn: &PgConnection, from_post_id: i32, my_user_id: Option<i32>) -> Result<Self, Error> {
173
174     use super::post_view::post_view::dsl::*;
175     use diesel::prelude::*;
176
177     let mut query = post_view.into_boxed();
178
179     query = query.filter(id.eq(from_post_id));
180
181     if let Some(my_user_id) = my_user_id {
182       query = query.filter(user_id.eq(my_user_id));
183     } else {
184       query = query.filter(user_id.is_null());
185     };
186
187     query.first::<Self>(conn)
188   }
189 }
190
191
192
193 #[cfg(test)]
194 mod tests {
195   use super::*;
196   use super::super::community::*;
197   use super::super::user::*;
198   use super::super::post::*;
199   #[test]
200   fn test_crud() {
201     let conn = establish_connection();
202
203     let user_name = "tegan".to_string();
204     let community_name = "test_community_3".to_string();
205     let post_name = "test post 3".to_string();
206
207     let new_user = UserForm {
208       name: user_name.to_owned(),
209       fedi_name: "rrf".into(),
210       preferred_username: None,
211       password_encrypted: "nope".into(),
212       email: None,
213       updated: None,
214       admin: false,
215       banned: false,
216       show_nsfw: false,
217     };
218
219     let inserted_user = User_::create(&conn, &new_user).unwrap();
220
221     let new_community = CommunityForm {
222       name: community_name.to_owned(),
223       title: "nada".to_owned(),
224       description: None,
225       creator_id: inserted_user.id,
226       category_id: 1,
227       removed: None,
228       deleted: None,
229       updated: None,
230       nsfw: false,
231     };
232
233     let inserted_community = Community::create(&conn, &new_community).unwrap();
234
235     let new_post = PostForm {
236       name: post_name.to_owned(),
237       url: None,
238       body: None,
239       creator_id: inserted_user.id,
240       community_id: inserted_community.id,
241       removed: None,
242       deleted: None,
243       locked: None,
244       updated: None,
245       nsfw: false,
246     };
247
248     let inserted_post = Post::create(&conn, &new_post).unwrap();
249
250     let post_like_form = PostLikeForm {
251       post_id: inserted_post.id,
252       user_id: inserted_user.id,
253       score: 1
254     };
255
256     let inserted_post_like = PostLike::like(&conn, &post_like_form).unwrap();
257
258     let expected_post_like = PostLike {
259       id: inserted_post_like.id,
260       post_id: inserted_post.id,
261       user_id: inserted_user.id,
262       published: inserted_post_like.published,
263       score: 1
264     };
265
266     let post_like_form = PostLikeForm {
267       post_id: inserted_post.id,
268       user_id: inserted_user.id,
269       score: 1
270     };
271
272     // the non user version
273     let expected_post_listing_no_user = PostView {
274       user_id: None,
275       my_vote: None,
276       id: inserted_post.id,
277       name: post_name.to_owned(),
278       url: None,
279       body: None,
280       creator_id: inserted_user.id,
281       creator_name: user_name.to_owned(),
282       community_id: inserted_community.id,
283       removed: false,
284       deleted: false,
285       locked: false,
286       community_name: community_name.to_owned(),
287       community_removed: false,
288       community_deleted: false,
289       community_nsfw: false,
290       number_of_comments: 0,
291       score: 1,
292       upvotes: 1,
293       downvotes: 0,
294       hot_rank: 1728,
295       published: inserted_post.published,
296       updated: None,
297       subscribed: None,
298       read: None,
299       saved: None,
300       nsfw: false,
301     };
302
303     let expected_post_listing_with_user = PostView {
304       user_id: Some(inserted_user.id),
305       my_vote: Some(1),
306       id: inserted_post.id,
307       name: post_name.to_owned(),
308       url: None,
309       body: None,
310       removed: false,
311       deleted: false,
312       locked: false,
313       creator_id: inserted_user.id,
314       creator_name: user_name.to_owned(),
315       community_id: inserted_community.id,
316       community_name: community_name.to_owned(),
317       community_removed: false,
318       community_deleted: false,
319       community_nsfw: false,
320       number_of_comments: 0,
321       score: 1,
322       upvotes: 1,
323       downvotes: 0,
324       hot_rank: 1728,
325       published: inserted_post.published,
326       updated: None,
327       subscribed: None,
328       read: None,
329       saved: None,
330       nsfw: false,
331     };
332
333
334     let read_post_listings_with_user = PostView::list(
335       &conn, 
336       PostListingType::Community, 
337       &SortType::New, 
338       Some(inserted_community.id), 
339       None, 
340       None,
341       None,
342       Some(inserted_user.id), 
343       false,
344       false, 
345       false, 
346       None, 
347       None).unwrap();
348     let read_post_listings_no_user = PostView::list(
349       &conn, 
350       PostListingType::Community, 
351       &SortType::New, 
352       Some(inserted_community.id), 
353       None, 
354       None, 
355       None,
356       None,
357       false,
358       false, 
359       false, 
360       None, 
361       None).unwrap();
362     let read_post_listing_no_user = PostView::read(&conn, inserted_post.id, None).unwrap();
363     let read_post_listing_with_user = PostView::read(&conn, inserted_post.id, Some(inserted_user.id)).unwrap();
364
365     let like_removed = PostLike::remove(&conn, &post_like_form).unwrap();
366     let num_deleted = Post::delete(&conn, inserted_post.id).unwrap();
367     Community::delete(&conn, inserted_community.id).unwrap();
368     User_::delete(&conn, inserted_user.id).unwrap();
369
370     // The with user
371     assert_eq!(expected_post_listing_with_user, read_post_listings_with_user[0]);
372     assert_eq!(expected_post_listing_with_user, read_post_listing_with_user);
373     assert_eq!(1, read_post_listings_with_user.len());
374
375     // Without the user
376     assert_eq!(expected_post_listing_no_user, read_post_listings_no_user[0]);
377     assert_eq!(expected_post_listing_no_user, read_post_listing_no_user);
378     assert_eq!(1, read_post_listings_no_user.len());
379
380     // assert_eq!(expected_post, inserted_post);
381     // assert_eq!(expected_post, updated_post);
382     assert_eq!(expected_post_like, inserted_post_like);
383     assert_eq!(1, like_removed);
384     assert_eq!(1, num_deleted);
385   }
386 }