]> Untitled Git - lemmy.git/blob - ui/src/components/communities.tsx
Squashed commit of the following:
[lemmy.git] / ui / src / components / communities.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
3 import { Subscription } from 'rxjs';
4 import { retryWhen, delay, take } from 'rxjs/operators';
5 import {
6   UserOperation,
7   Community,
8   ListCommunitiesResponse,
9   CommunityResponse,
10   FollowCommunityForm,
11   ListCommunitiesForm,
12   SortType,
13   WebSocketJsonResponse,
14 } from '../interfaces';
15 import { WebSocketService } from '../services';
16 import { wsJsonToRes, toast } from '../utils';
17 import { i18n } from '../i18next';
18 import { T } from 'inferno-i18next';
19
20 declare const Sortable: any;
21
22 const communityLimit = 100;
23
24 interface CommunitiesState {
25   communities: Array<Community>;
26   page: number;
27   loading: boolean;
28 }
29
30 export class Communities extends Component<any, CommunitiesState> {
31   private subscription: Subscription;
32   private emptyState: CommunitiesState = {
33     communities: [],
34     loading: true,
35     page: this.getPageFromProps(this.props),
36   };
37
38   constructor(props: any, context: any) {
39     super(props, context);
40     this.state = this.emptyState;
41     this.subscription = WebSocketService.Instance.subject
42       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
43       .subscribe(
44         msg => this.parseMessage(msg),
45         err => console.error(err),
46         () => console.log('complete')
47       );
48
49     this.refetch();
50   }
51
52   getPageFromProps(props: any): number {
53     return props.match.params.page ? Number(props.match.params.page) : 1;
54   }
55
56   componentWillUnmount() {
57     this.subscription.unsubscribe();
58   }
59
60   componentDidMount() {
61     document.title = `${i18n.t('communities')} - ${
62       WebSocketService.Instance.site.name
63     }`;
64   }
65
66   // Necessary for back button for some reason
67   componentWillReceiveProps(nextProps: any) {
68     if (nextProps.history.action == 'POP') {
69       this.state = this.emptyState;
70       this.state.page = this.getPageFromProps(nextProps);
71       this.refetch();
72     }
73   }
74
75   render() {
76     return (
77       <div class="container">
78         {this.state.loading ? (
79           <h5 class="">
80             <svg class="icon icon-spinner spin">
81               <use xlinkHref="#icon-spinner"></use>
82             </svg>
83           </h5>
84         ) : (
85           <div>
86             <h5>
87               <T i18nKey="list_of_communities">#</T>
88             </h5>
89             <div class="table-responsive">
90               <table id="community_table" class="table table-sm table-hover">
91                 <thead class="pointer">
92                   <tr>
93                     <th>
94                       <T i18nKey="name">#</T>
95                     </th>
96                     <th class="d-none d-lg-table-cell">
97                       <T i18nKey="title">#</T>
98                     </th>
99                     <th>
100                       <T i18nKey="category">#</T>
101                     </th>
102                     <th class="text-right">
103                       <T i18nKey="subscribers">#</T>
104                     </th>
105                     <th class="text-right d-none d-lg-table-cell">
106                       <T i18nKey="posts">#</T>
107                     </th>
108                     <th class="text-right d-none d-lg-table-cell">
109                       <T i18nKey="comments">#</T>
110                     </th>
111                     <th></th>
112                   </tr>
113                 </thead>
114                 <tbody>
115                   {this.state.communities.map(community => (
116                     <tr>
117                       <td>
118                         <Link to={`/c/${community.name}`}>
119                           {community.name}
120                         </Link>
121                       </td>
122                       <td class="d-none d-lg-table-cell">{community.title}</td>
123                       <td>{community.category_name}</td>
124                       <td class="text-right">
125                         {community.number_of_subscribers}
126                       </td>
127                       <td class="text-right d-none d-lg-table-cell">
128                         {community.number_of_posts}
129                       </td>
130                       <td class="text-right d-none d-lg-table-cell">
131                         {community.number_of_comments}
132                       </td>
133                       <td class="text-right">
134                         {community.subscribed ? (
135                           <span
136                             class="pointer btn-link"
137                             onClick={linkEvent(
138                               community.id,
139                               this.handleUnsubscribe
140                             )}
141                           >
142                             <T i18nKey="unsubscribe">#</T>
143                           </span>
144                         ) : (
145                           <span
146                             class="pointer btn-link"
147                             onClick={linkEvent(
148                               community.id,
149                               this.handleSubscribe
150                             )}
151                           >
152                             <T i18nKey="subscribe">#</T>
153                           </span>
154                         )}
155                       </td>
156                     </tr>
157                   ))}
158                 </tbody>
159               </table>
160             </div>
161             {this.paginator()}
162           </div>
163         )}
164       </div>
165     );
166   }
167
168   paginator() {
169     return (
170       <div class="mt-2">
171         {this.state.page > 1 && (
172           <button
173             class="btn btn-sm btn-secondary mr-1"
174             onClick={linkEvent(this, this.prevPage)}
175           >
176             <T i18nKey="prev">#</T>
177           </button>
178         )}
179         {this.state.communities.length == communityLimit && (
180           <button
181             class="btn btn-sm btn-secondary"
182             onClick={linkEvent(this, this.nextPage)}
183           >
184             <T i18nKey="next">#</T>
185           </button>
186         )}
187       </div>
188     );
189   }
190
191   updateUrl() {
192     this.props.history.push(`/communities/page/${this.state.page}`);
193   }
194
195   nextPage(i: Communities) {
196     i.state.page++;
197     i.setState(i.state);
198     i.updateUrl();
199     i.refetch();
200   }
201
202   prevPage(i: Communities) {
203     i.state.page--;
204     i.setState(i.state);
205     i.updateUrl();
206     i.refetch();
207   }
208
209   handleUnsubscribe(communityId: number) {
210     let form: FollowCommunityForm = {
211       community_id: communityId,
212       follow: false,
213     };
214     WebSocketService.Instance.followCommunity(form);
215   }
216
217   handleSubscribe(communityId: number) {
218     let form: FollowCommunityForm = {
219       community_id: communityId,
220       follow: true,
221     };
222     WebSocketService.Instance.followCommunity(form);
223   }
224
225   refetch() {
226     let listCommunitiesForm: ListCommunitiesForm = {
227       sort: SortType[SortType.TopAll],
228       limit: communityLimit,
229       page: this.state.page,
230     };
231
232     WebSocketService.Instance.listCommunities(listCommunitiesForm);
233   }
234
235   parseMessage(msg: WebSocketJsonResponse) {
236     console.log(msg);
237     let res = wsJsonToRes(msg);
238     if (res.error) {
239       toast(i18n.t(msg.error), 'danger');
240       return;
241     } else if (res.op == UserOperation.ListCommunities) {
242       let data = res.data as ListCommunitiesResponse;
243       this.state.communities = data.communities;
244       this.state.communities.sort(
245         (a, b) => b.number_of_subscribers - a.number_of_subscribers
246       );
247       this.state.loading = false;
248       window.scrollTo(0, 0);
249       this.setState(this.state);
250       let table = document.querySelector('#community_table');
251       Sortable.initTable(table);
252     } else if (res.op == UserOperation.FollowCommunity) {
253       let data = res.data as CommunityResponse;
254       let found = this.state.communities.find(c => c.id == data.community.id);
255       found.subscribed = data.community.subscribed;
256       found.number_of_subscribers = data.community.number_of_subscribers;
257       this.setState(this.state);
258     }
259   }
260 }