]> Untitled Git - lemmy.git/commitdiff
Adding support for community and user searching.
authorDessalines <tyhou13@gmx.com>
Sat, 10 Aug 2019 17:32:06 +0000 (10:32 -0700)
committerDessalines <tyhou13@gmx.com>
Sat, 10 Aug 2019 17:32:06 +0000 (10:32 -0700)
- Fixes #130

README.md
server/src/api/community.rs
server/src/api/site.rs
server/src/db/community_view.rs
server/src/db/mod.rs
server/src/db/user_view.rs
ui/src/components/search.tsx
ui/src/interfaces.ts
ui/src/translations/en.ts

index ed21cfe3d291d3ca79066fa8ec8101959a2fe0f4..b02595794e43f9b7126ca8ad16d71b28f02a404a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@ Front Page|Post
   - Can lock, remove, and restore posts and comments.
   - Can ban and unban users from communities and the site.
 - Clean, mobile-friendly interface.
+- i18n / internationalization support.
 - High performance.
   - Server is written in rust.
   - Front end is `~80kB` gzipped.
index fe225794233d2631420385939a1ea96395f1796f..ca73de49c9d458939289f4ef16fecc9b7d6e1e0c 100644 (file)
@@ -348,7 +348,7 @@ impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
 
     let sort = SortType::from_str(&data.sort)?;
 
-    let communities: Vec<CommunityView> = CommunityView::list(&conn, user_id, sort, data.page, data.limit)?;
+    let communities: Vec<CommunityView> = CommunityView::list(&conn, &sort, user_id, None, data.page, data.limit)?;
 
     // Return the jwt
     Ok(
index 08fefae45fc81545defb47cdcb6f48b9a0591e3d..09af742fae5327a371210cb699621ed984924f94 100644 (file)
@@ -25,6 +25,8 @@ pub struct SearchResponse {
   op: String,
   comments: Vec<CommentView>,
   posts: Vec<PostView>,
+  communities: Vec<CommunityView>,
+  users: Vec<UserView>,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -272,53 +274,89 @@ impl Perform<SearchResponse> for Oper<Search> {
 
     let mut posts = Vec::new();
     let mut comments = Vec::new();
+    let mut communities = Vec::new();
+    let mut users = Vec::new();
 
     match type_ {
       SearchType::Posts => {
-        posts = PostView::list(&conn, 
-                               PostListingType::All, 
-                               &sort, 
-                               data.community_id, 
-                               None,
-                               Some(data.q.to_owned()),
-                               None, 
-                               false, 
-                               false, 
-                               data.page, 
-                               data.limit)?;
+        posts = PostView::list(
+          &conn, 
+          PostListingType::All, 
+          &sort, 
+          data.community_id, 
+          None,
+          Some(data.q.to_owned()),
+          None, 
+          false, 
+          false, 
+          data.page, 
+          data.limit)?;
       },
       SearchType::Comments => {
-        comments = CommentView::list(&conn, 
-                                     &sort, 
-                                     None, 
-                                     None, 
-                                     Some(data.q.to_owned()),
-                                     None,
-                                     false, 
-                                     data.page,
-                                     data.limit)?;
+        comments = CommentView::list(
+          &conn, 
+          &sort, 
+          None, 
+          None, 
+          Some(data.q.to_owned()),
+          None,
+          false, 
+          data.page,
+          data.limit)?;
+      },
+      SearchType::Communities => {
+        communities = CommunityView::list(
+          &conn, 
+          &sort, 
+          None, 
+          Some(data.q.to_owned()),
+          data.page, 
+          data.limit)?;
+      }, 
+      SearchType::Users => {
+        users = UserView::list(
+          &conn, 
+          &sort, 
+          Some(data.q.to_owned()), 
+          data.page, 
+          data.limit)?;
       }, 
-      SearchType::Both => {
-        posts = PostView::list(&conn, 
-                               PostListingType::All, 
-                               &sort, 
-                               data.community_id, 
-                               None,
-                               Some(data.q.to_owned()),
-                               None, 
-                               false, 
-                               false, 
-                               data.page, 
-                               data.limit)?;
-        comments = CommentView::list(&conn, 
-                                     &sort, 
-                                     None, 
-                                     None, 
-                                     Some(data.q.to_owned()),
-                                     None,
-                                     false, 
-                                     data.page,
-                                     data.limit)?;
+      SearchType::All => {
+        posts = PostView::list(
+          &conn, 
+          PostListingType::All, 
+          &sort, 
+          data.community_id, 
+          None,
+          Some(data.q.to_owned()),
+          None, 
+          false, 
+          false, 
+          data.page, 
+          data.limit)?;
+        comments = CommentView::list(
+          &conn, 
+          &sort, 
+          None, 
+          None, 
+          Some(data.q.to_owned()),
+          None,
+          false, 
+          data.page,
+          data.limit)?;
+        communities = CommunityView::list(
+          &conn, 
+          &sort, 
+          None, 
+          Some(data.q.to_owned()),
+          data.page, 
+          data.limit)?;
+        users = UserView::list(
+          &conn, 
+          &sort, 
+          Some(data.q.to_owned()), 
+          data.page, 
+          data.limit)?;
       }
     };
 
@@ -329,6 +367,8 @@ impl Perform<SearchResponse> for Oper<Search> {
         op: self.op.to_string(),
         comments: comments,
         posts: posts,
+        communities: communities,
+        users: users,
       }
       )
   }
index ff0fc89b61324160dd3b2bb12cf59ffeacf7e11c..6249090d77978dfa68da64c8b7ad62c7386c21ca 100644 (file)
@@ -113,8 +113,9 @@ impl CommunityView {
   }
 
   pub fn list(conn: &PgConnection, 
+              sort: &SortType, 
               from_user_id: Option<i32>, 
-              sort: SortType, 
+              search_term: Option<String>,
               page: Option<i64>,
               limit: Option<i64>,
               ) -> Result<Vec<Self>, Error> {
@@ -123,6 +124,10 @@ impl CommunityView {
 
     let (limit, offset) = limit_and_offset(page, limit);
 
+    if let Some(search_term) = search_term {
+      query = query.filter(name.ilike(fuzzy_search(&search_term)));
+    };
+
     // The view lets you pass a null user_id, if you're not logged in
     match sort {
       SortType::Hot => query = query.order_by(hot_rank.desc())
index e0b7c8567c24399a407817380eb6a4d26c6778b2..9f0c79b8fc47209b3e06f8c280a38bbd3995306c 100644 (file)
@@ -67,7 +67,7 @@ pub enum SortType {
 
 #[derive(EnumString,ToString,Debug, Serialize, Deserialize)]
 pub enum SearchType {
-  Both, Comments, Posts
+  All, Comments, Posts, Communities, Users
 }
 
 pub fn fuzzy_search(q: &str) -> String {
index 3d78ae1a0aa47a84c9e35c85895938f5330c70cf..897ee23ada1bc5e31eefc5f3ebd316be832866ee 100644 (file)
@@ -31,6 +31,49 @@ pub struct UserView {
 }
 
 impl UserView {
+
+  pub fn list(conn: &PgConnection, 
+              sort: &SortType, 
+              search_term: Option<String>,
+              page: Option<i64>,
+              limit: Option<i64>,
+              ) -> Result<Vec<Self>, Error> {
+    use super::user_view::user_view::dsl::*;
+
+    let (limit, offset) = limit_and_offset(page, limit);
+
+    let mut query = user_view.into_boxed();
+
+    if let Some(search_term) = search_term {
+      query = query.filter(name.ilike(fuzzy_search(&search_term)));
+    };
+
+    query = match sort {
+      SortType::Hot => query.order_by(comment_score.desc())
+        .then_order_by(published.desc()),
+      SortType::New => query.order_by(published.desc()),
+      SortType::TopAll => query.order_by(comment_score.desc()),
+      SortType::TopYear => query
+        .filter(published.gt(now - 1.years()))
+        .order_by(comment_score.desc()),
+        SortType::TopMonth => query
+          .filter(published.gt(now - 1.months()))
+          .order_by(comment_score.desc()),
+          SortType::TopWeek => query
+            .filter(published.gt(now - 1.weeks()))
+            .order_by(comment_score.desc()),
+            SortType::TopDay => query
+              .filter(published.gt(now - 1.days()))
+              .order_by(comment_score.desc())
+    };
+
+    query = query
+      .limit(limit)
+      .offset(offset);
+
+    query.load::<Self>(conn) 
+  }
+
   pub fn read(conn: &PgConnection, from_user_id: i32) -> Result<Self, Error> {
     use super::user_view::user_view::dsl::*;
 
index 01122fd437373a7861977fe0dadb33ee39d2e010..0f8727cb875b5d537db9543233d9ba6b98188c73 100644 (file)
@@ -1,7 +1,8 @@
 import { Component, linkEvent } from 'inferno';
+import { Link } from 'inferno-router';
 import { Subscription } from "rxjs";
 import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Post, Comment, SortType, SearchForm, SearchResponse, SearchType } from '../interfaces';
+import { UserOperation, Post, Comment, Community, UserView, SortType, SearchForm, SearchResponse, SearchType } from '../interfaces';
 import { WebSocketService } from '../services';
 import { msgOp, fetchLimit } from '../utils';
 import { PostListing } from './post-listing';
@@ -23,13 +24,15 @@ export class Search extends Component<any, SearchState> {
   private subscription: Subscription;
   private emptyState: SearchState = {
     q: undefined,
-    type_: SearchType.Both,
+    type_: SearchType.All,
     sort: SortType.TopAll,
     page: 1,
     searchResponse: {
       op: null,
       posts: [],
       comments: [],
+      communities: [],
+      users: [],
     },
     loading: false,
   }
@@ -65,8 +68,8 @@ export class Search extends Component<any, SearchState> {
             <h5><T i18nKey="search">#</T></h5>
             {this.selects()}
             {this.searchForm()}
-            {this.state.type_ == SearchType.Both &&
-              this.both()
+            {this.state.type_ == SearchType.All &&
+              this.all()
             }
             {this.state.type_ == SearchType.Comments &&
               this.comments()
@@ -74,6 +77,12 @@ export class Search extends Component<any, SearchState> {
             {this.state.type_ == SearchType.Posts &&
               this.posts()
             }
+            {this.state.type_ == SearchType.Communities &&
+              this.communities()
+            }
+            {this.state.type_ == SearchType.Users &&
+              this.users()
+            }
             {this.noResults()}
             {this.paginator()}
           </div>
@@ -101,9 +110,11 @@ export class Search extends Component<any, SearchState> {
       <div className="mb-2">
         <select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="custom-select custom-select-sm w-auto">
           <option disabled><T i18nKey="type">#</T></option>
-          <option value={SearchType.Both}><T i18nKey="both">#</T></option>
+          <option value={SearchType.All}><T i18nKey="all">#</T></option>
           <option value={SearchType.Comments}><T i18nKey="comments">#</T></option>
           <option value={SearchType.Posts}><T i18nKey="posts">#</T></option>
+          <option value={SearchType.Communities}><T i18nKey="communities">#</T></option>
+          <option value={SearchType.Users}><T i18nKey="users">#</T></option>
         </select>
         <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
           <option disabled><T i18nKey="sort_type">#</T></option>
@@ -119,28 +130,51 @@ export class Search extends Component<any, SearchState> {
 
   }
 
-  both() {
-    let combined: Array<{type_: string, data: Comment | Post}> = [];
+  all() {
+    let combined: Array<{type_: string, data: Comment | Post | Community | UserView}> = [];
     let comments = this.state.searchResponse.comments.map(e => {return {type_: "comments", data: e}});
     let posts = this.state.searchResponse.posts.map(e => {return {type_: "posts", data: e}});
+    let communities = this.state.searchResponse.communities.map(e => {return {type_: "communities", data: e}});
+    let users = this.state.searchResponse.users.map(e => {return {type_: "users", data: e}});
 
     combined.push(...comments);
     combined.push(...posts);
+    combined.push(...communities);
+    combined.push(...users);
 
     // Sort it
     if (this.state.sort == SortType.New) {
       combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
     } else {
-      combined.sort((a, b) => b.data.score - a.data.score);
+      combined.sort((a, b) => ((b.data as Comment | Post).score 
+        | (b.data as Community).number_of_subscribers
+          | (b.data as UserView).comment_score) 
+          - ((a.data as Comment | Post).score 
+            | (a.data as Community).number_of_subscribers 
+              | (a.data as UserView).comment_score));
     }
 
     return (
       <div>
         {combined.map(i =>
           <div>
-            {i.type_ == "posts"
-              ? <PostListing post={i.data as Post} showCommunity viewOnly />
-              : <CommentNodes nodes={[{comment: i.data as Comment}]} viewOnly noIndent />
+            {i.type_ == "posts" &&
+              <PostListing post={i.data as Post} showCommunity viewOnly />
+            }
+            {i.type_ == "comments" && 
+              <CommentNodes nodes={[{comment: i.data as Comment}]} viewOnly noIndent />
+            }
+            {i.type_ == "communities" && 
+              <div>
+                <span><Link to={`/c/${(i.data as Community).name}`}>{`/c/${(i.data as Community).name}`}</Link></span>
+                <span>{` - ${(i.data as Community).title} - ${(i.data as Community).number_of_subscribers} subscribers`}</span>
+              </div>
+            }
+            {i.type_ == "users" && 
+              <div>
+                <span><Link className="text-info" to={`/u/${(i.data as UserView).name}`}>{`/u/${(i.data as UserView).name}`}</Link></span>
+                <span>{` - ${(i.data as UserView).comment_score} comment karma`}</span>
+              </div>
             }
           </div>
                      )
@@ -169,6 +203,33 @@ export class Search extends Component<any, SearchState> {
     );
   }
 
+  // Todo possibly create UserListing and CommunityListing
+  communities() {
+    return (
+      <div>
+        {this.state.searchResponse.communities.map(community => 
+          <div>
+            <span><Link to={`/c/${community.name}`}>{`/c/${community.name}`}</Link></span>
+            <span>{` - ${community.title} - ${community.number_of_subscribers} subscribers`}</span>
+          </div>
+        )}
+      </div>
+    );
+  }
+
+  users() {
+    return (
+      <div>
+        {this.state.searchResponse.users.map(user => 
+          <div>
+            <span><Link className="text-info" to={`/u/${user.name}`}>{`/u/${user.name}`}</Link></span>
+            <span>{` - ${user.comment_score} comment karma`}</span>
+          </div>
+        )}
+      </div>
+    );
+  }
+
   paginator() {
     return (
       <div class="mt-2">
@@ -220,14 +281,12 @@ export class Search extends Component<any, SearchState> {
     i.state.sort = Number(event.target.value);
     i.state.page = 1;
     i.setState(i.state);
-    i.search();
   }
 
   handleTypeChange(i: Search, event: any) {
     i.state.type_ = Number(event.target.value);
     i.state.page = 1;
     i.setState(i.state);
-    i.search();
   }
 
   handleSearchSubmit(i: Search, event: any) {
index 717595d7bdf53f698f495efb906e1d01b6571bdd..59f4ba1c91fef07ef2a9c9494d90c5b05037892e 100644 (file)
@@ -15,7 +15,7 @@ export enum SortType {
 }
 
 export enum SearchType {
-  Both, Comments, Posts
+  All, Comments, Posts, Communities, Users
 }
 
 export interface User {
@@ -542,4 +542,6 @@ export interface SearchResponse {
   op: string;
   posts?: Array<Post>;
   comments?: Array<Comment>;
+  communities: Array<Community>;  
+  users: Array<UserView>;
 }
index 37bbb8276d5d2eb44a98c35461972cdd412d84fd..7c2b184f18b7624a657eaa297fe372f61cfe021d 100644 (file)
@@ -12,6 +12,7 @@ export const en = {
     number_of_comments:'{{count}} Comments',
     remove_comment: 'Remove Comment',
     communities: 'Communities',
+    users: 'Users',
     create_a_community: 'Create a community',
     create_community: 'Create Community',
     remove_community: 'Remove Community',