]> Untitled Git - lemmy.git/commitdiff
Reworking some UI. Adding proper trending communities with hot rank.
authorDessalines <tyhou13@gmx.com>
Thu, 2 May 2019 05:26:31 +0000 (22:26 -0700)
committerDessalines <tyhou13@gmx.com>
Thu, 2 May 2019 05:26:31 +0000 (22:26 -0700)
- Breaking out subscribed and all into radios. Fixes #142

13 files changed:
README.md
server/migrations/2019-05-02-051656_community_view_hot_rank/down.sql [new file with mode: 0644]
server/migrations/2019-05-02-051656_community_view_hot_rank/up.sql [new file with mode: 0644]
server/src/actions/community_view.rs
server/src/websocket_server/server.rs
ui/src/components/communities.tsx
ui/src/components/community.tsx
ui/src/components/inbox.tsx
ui/src/components/login.tsx
ui/src/components/main.tsx
ui/src/components/search.tsx
ui/src/components/user.tsx
ui/src/index.tsx

index 94238136f2caea60b5b42f73c97dd01eaffd5a91..957462fc15a45aa5cbd90831935b17c01e967e70 100644 (file)
--- a/README.md
+++ b/README.md
@@ -46,7 +46,8 @@ Each lemmy server can set its own moderation policy; appointing site-wide admins
 
 ## Why's it called Lemmy?
 - Lead singer from [motorhead](https://invidio.us/watch?v=pWB5JZRGl0U).
-- The old school [video game](https://en.wikipedia.org/wiki/Lemmings_(video_game)).
+- The old school [video game](<https://en.wikipedia.org/wiki/Lemmings_(video_game)>).
+- The [Koopa from Super Mario](https://www.mariowiki.com/Lemmy_Koopa).
 - The [furry rodents](http://sunchild.fpwc.org/lemming-the-little-giant-of-the-north/).
 
 Made with [Rust](https://www.rust-lang.org), [Actix](https://actix.rs/), [Inferno](https://www.infernojs.org), [Typescript](https://www.typescriptlang.org/) and [Diesel](http://diesel.rs/).
diff --git a/server/migrations/2019-05-02-051656_community_view_hot_rank/down.sql b/server/migrations/2019-05-02-051656_community_view_hot_rank/down.sql
new file mode 100644 (file)
index 0000000..0f3a58a
--- /dev/null
@@ -0,0 +1,28 @@
+drop view community_view;
+create view community_view as 
+with all_community as
+(
+  select *,
+  (select name from user_ u where c.creator_id = u.id) as creator_name,
+  (select name from category ct where c.category_id = ct.id) as category_name,
+  (select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
+  (select count(*) from post p where p.community_id = c.id) as number_of_posts,
+  (select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments
+  from community c
+)
+
+select
+ac.*,
+u.id as user_id,
+(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
+from user_ u
+cross join all_community ac
+
+union all
+
+select 
+ac.*,
+null as user_id,
+null as subscribed
+from all_community ac
+;
diff --git a/server/migrations/2019-05-02-051656_community_view_hot_rank/up.sql b/server/migrations/2019-05-02-051656_community_view_hot_rank/up.sql
new file mode 100644 (file)
index 0000000..e7e7536
--- /dev/null
@@ -0,0 +1,29 @@
+drop view community_view;
+create view community_view as 
+with all_community as
+(
+  select *,
+  (select name from user_ u where c.creator_id = u.id) as creator_name,
+  (select name from category ct where c.category_id = ct.id) as category_name,
+  (select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
+  (select count(*) from post p where p.community_id = c.id) as number_of_posts,
+  (select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments,
+  hot_rank((select count(*) from community_follower cf where cf.community_id = c.id), c.published) as hot_rank
+  from community c
+)
+
+select
+ac.*,
+u.id as user_id,
+(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
+from user_ u
+cross join all_community ac
+
+union all
+
+select 
+ac.*,
+null as user_id,
+null as subscribed
+from all_community ac
+;
index a52897ff653f730a8ea9688044be876aea750d1e..9a162746a03ae927336008ebfff60baf01bf6ef9 100644 (file)
@@ -21,6 +21,7 @@ table! {
     number_of_subscribers -> BigInt,
     number_of_posts -> BigInt,
     number_of_comments -> BigInt,
+    hot_rank -> Int4,
     user_id -> Nullable<Int4>,
     subscribed -> Nullable<Bool>,
   }
@@ -92,6 +93,7 @@ pub struct CommunityView {
   pub number_of_subscribers: i64,
   pub number_of_posts: i64,
   pub number_of_comments: i64,
+  pub hot_rank: i32,
   pub user_id: Option<i32>,
   pub subscribed: Option<bool>,
 }
@@ -127,6 +129,7 @@ impl CommunityView {
 
     // 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()).filter(user_id.is_null()),
       SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()),
       SortType::TopAll => {
         match from_user_id {
index b2416951361b841e4bf311b46757e94eda183b9f..aaeae132b295aeb2e84e0824a3e4f02eab9af270 100644 (file)
@@ -573,6 +573,7 @@ impl ChatServer {
   fn check_rate_limit_full(&mut self, addr: usize, rate: i32, per: i32) -> Result<(), Error> {
     if let Some(info) = self.sessions.get(&addr) {
       if let Some(rate_limit) = self.rate_limits.get_mut(&info.ip) {
+        // The initial value
         if rate_limit.allowance == -2f64 {
           rate_limit.allowance = rate as f64;
         };
@@ -625,7 +626,7 @@ impl Handler<Connect> for ChatServer {
 
     // register session with random id
     let id = self.rng.gen::<usize>();
-    println!("{} Joined", &msg.ip);
+    println!("{} joined", &msg.ip);
 
     self.sessions.insert(id, SessionInfo {
       addr: msg.addr,
index 03e124f17e097331b7bfb5cfc4f6725aca1917d5..190f8e3d6ce614ce2203b69c1a8942cb088c3d4f 100644 (file)
@@ -66,17 +66,17 @@ export class Communities extends Component<any, CommunitiesState> {
         {this.state.loading ? 
         <h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> : 
         <div>
-          <h5>Communities</h5>
+          <h5>List of communities</h5>
           <div class="table-responsive">
             <table id="community_table" class="table table-sm table-hover">
               <thead class="pointer">
                 <tr>
                   <th>Name</th>
-                  <th>Title</th>
+                  <th class="d-none d-lg-table-cell">Title</th>
                   <th>Category</th>
-                  <th class="text-right d-none d-md-table-cell">Subscribers</th>
-                  <th class="text-right d-none d-md-table-cell">Posts</th>
-                  <th class="text-right d-none d-md-table-cell">Comments</th>
+                  <th class="text-right">Subscribers</th>
+                  <th class="text-right d-none d-lg-table-cell">Posts</th>
+                  <th class="text-right d-none d-lg-table-cell">Comments</th>
                   <th></th>
                 </tr>
               </thead>
@@ -84,11 +84,11 @@ export class Communities extends Component<any, CommunitiesState> {
                 {this.state.communities.map(community =>
                   <tr>
                     <td><Link to={`/c/${community.name}`}>{community.name}</Link></td>
-                    <td>{community.title}</td>
+                    <td class="d-none d-lg-table-cell">{community.title}</td>
                     <td>{community.category_name}</td>
-                    <td class="text-right d-none d-md-table-cell">{community.number_of_subscribers}</td>
-                    <td class="text-right d-none d-md-table-cell">{community.number_of_posts}</td>
-                    <td class="text-right d-none d-md-table-cell">{community.number_of_comments}</td>
+                    <td class="text-right">{community.number_of_subscribers}</td>
+                    <td class="text-right d-none d-lg-table-cell">{community.number_of_posts}</td>
+                    <td class="text-right d-none d-lg-table-cell">{community.number_of_comments}</td>
                     <td class="text-right">
                       {community.subscribed ? 
                       <span class="pointer btn-link" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</span> : 
index fa8ae43b560293e404a493bdeb63fb0bb91585a2..93cdbd92fc5e6149c33b9207b5f4ba15b65d232b 100644 (file)
@@ -124,7 +124,7 @@ export class Community extends Component<any, State> {
   selects() {
     return (
       <div className="mb-2">
-        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto">
+        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto">
           <option disabled>Sort Type</option>
           <option value={SortType.Hot}>Hot</option>
           <option value={SortType.New}>New</option>
index 48a9d36643b8bad0667ccee0acd2b2fed57a177c..659a4a20d52ad11708e61643ee60c41015f8d79b 100644 (file)
@@ -80,12 +80,12 @@ export class Inbox extends Component<any, InboxState> {
   selects() {
     return (
       <div className="mb-2">
-        <select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select w-auto">
+        <select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select custom-select-sm w-auto">
           <option disabled>Type</option>
           <option value={UnreadType.Unread}>Unread</option>
           <option value={UnreadType.All}>All</option>
         </select>
-        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto ml-2">
+        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
           <option disabled>Sort Type</option>
           <option value={SortType.New}>New</option>
           <option value={SortType.TopDay}>Top Day</option>
index 5c0b8dd1d9364c5e30a278659105955e5b96aa32..b2ad70a16e46466375177dd66e6904a75c504a06 100644 (file)
@@ -95,7 +95,7 @@ export class Login extends Component<any, State> {
             </div>
           </div>
         </form>
-        Forgot your password or deleted your account? Reset your password. TODO
+        {/* Forgot your password or deleted your account? Reset your password. TODO */}
       </div>
     );
   }
@@ -161,7 +161,6 @@ export class Login extends Component<any, State> {
     event.preventDefault();
     i.state.registerLoading = true;
     i.setState(i.state);
-    event.preventDefault();
 
     let endTimer = new Date().getTime();
     let elapsed = endTimer - i.state.registerForm.spam_timeri;
@@ -209,14 +208,14 @@ export class Login extends Component<any, State> {
       return;
     } else {
       if (op == UserOperation.Login) {
-        this.state.loginLoading = false;
-        this.state.registerLoading = false;
+        this.state = this.emptyState;
+        this.setState(this.state);
         let res: LoginResponse = msg;
         UserService.Instance.login(res);
         this.props.history.push('/');
       } else if (op == UserOperation.Register) {
-        this.state.loginLoading = false;
-        this.state.registerLoading = false;
+        this.state = this.emptyState;
+        this.setState(this.state);
         let res: LoginResponse = msg;
         UserService.Instance.login(res);
         this.props.history.push('/communities');
index 44025fef609975c86e65ab632d081a0f61d4003c..cd679ecb9e92adde521415d6d5810a5f7ac382b7 100644 (file)
@@ -87,7 +87,7 @@ export class Main extends Component<any, MainState> {
     }
 
     let listCommunitiesForm: ListCommunitiesForm = {
-      sort: SortType[SortType.New],
+      sort: SortType[SortType.Hot],
       limit: 6
     }
 
@@ -247,7 +247,29 @@ export class Main extends Component<any, MainState> {
   selects() {
     return (
       <div className="mb-2">
-        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto">
+        <div class="btn-group btn-group-toggle">
+          <label className={`btn btn-sm btn-secondary 
+            ${this.state.type_ == ListingType.Subscribed && 'active'}
+            ${UserService.Instance.user == undefined ? 'disabled' : 'pointer'}
+            `}>
+            <input type="radio" 
+              value={ListingType.Subscribed}
+              checked={this.state.type_ == ListingType.Subscribed}
+              onChange={linkEvent(this, this.handleTypeChange)}
+              disabled={UserService.Instance.user == undefined}
+            />
+            Subscribed
+          </label>
+          <label className={`pointer btn btn-sm btn-secondary ${this.state.type_ == ListingType.All && 'active'}`}>
+            <input type="radio" 
+              value={ListingType.All}
+              checked={this.state.type_ == ListingType.All}
+              onChange={linkEvent(this, this.handleTypeChange)}
+            /> 
+            All
+          </label>
+        </div>
+        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="ml-2 custom-select custom-select-sm w-auto">
           <option disabled>Sort Type</option>
           <option value={SortType.Hot}>Hot</option>
           <option value={SortType.New}>New</option>
@@ -258,14 +280,6 @@ export class Main extends Component<any, MainState> {
           <option value={SortType.TopYear}>Year</option>
           <option value={SortType.TopAll}>All</option>
         </select>
-        { UserService.Instance.user &&
-          <select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="ml-2 custom-select w-auto">
-            <option disabled>Type</option>
-            <option value={ListingType.All}>All</option>
-            <option value={ListingType.Subscribed}>Subscribed</option>
-          </select>
-
-        }
       </div>
     )
   }
index a4b389e7ab87ab3a3d702ad899e9244958fa1920..f066f6ed63a376c392babe0bff7f422d2580343c 100644 (file)
@@ -97,13 +97,13 @@ export class Search extends Component<any, SearchState> {
   selects() {
     return (
       <div className="mb-2">
-        <select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="custom-select w-auto">
+        <select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="custom-select custom-select-sm w-auto">
           <option disabled>Type</option>
           <option value={SearchType.Both}>Both</option>
           <option value={SearchType.Comments}>Comments</option>
           <option value={SearchType.Posts}>Posts</option>
         </select>
-        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto ml-2">
+        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
           <option disabled>Sort Type</option>
           <option value={SortType.New}>New</option>
           <option value={SortType.TopDay}>Top Day</option>
index b55bf41cd2b1ef2232defdc7f3f9ba8b7c31f6fa..92ad53359afe99582c457690df0b83d2eebe0b82 100644 (file)
@@ -141,14 +141,14 @@ export class User extends Component<any, UserState> {
   selects() {
     return (
       <div className="mb-2">
-        <select value={this.state.view} onChange={linkEvent(this, this.handleViewChange)} class="custom-select w-auto">
+        <select value={this.state.view} onChange={linkEvent(this, this.handleViewChange)} class="custom-select custom-select-sm w-auto">
           <option disabled>View</option>
           <option value={View.Overview}>Overview</option>
           <option value={View.Comments}>Comments</option>
           <option value={View.Posts}>Posts</option>
           <option value={View.Saved}>Saved</option>
         </select>
-        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto ml-2">
+        <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
           <option disabled>Sort Type</option>
           <option value={SortType.New}>New</option>
           <option value={SortType.TopDay}>Top Day</option>
index 76605d8b8d7f5ca4050020da57fc7e8b6ec13ac7..b744ac7d2963d138dd8220813b24fd7a49cc80c8 100644 (file)
@@ -36,7 +36,7 @@ class Index extends Component<any, any> {
     return (
       <HashRouter>
         <Navbar />
-        <div class="mt-3 p-0">
+        <div class="mt-1 p-0">
           <Switch>
             <Route path={`/home/type/:type/sort/:sort/page/:page`} component={Main} />
             <Route exact path={`/`} component={Main} />