]> Untitled Git - lemmy.git/blob - ui/src/components/community.tsx
Spanish translations
[lemmy.git] / ui / src / components / community.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Subscription } from "rxjs";
3 import { retryWhen, delay, take } from 'rxjs/operators';
4 import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse,  CommunityUser, UserView, SortType, Post, GetPostsForm, ListingType, GetPostsResponse, CreatePostLikeResponse } from '../interfaces';
5 import { WebSocketService } from '../services';
6 import { PostListings } from './post-listings';
7 import { Sidebar } from './sidebar';
8 import { msgOp, routeSortTypeToEnum, fetchLimit, postRefetchSeconds } from '../utils';
9 import { T, i18n } from 'inferno-i18next';
10
11 interface State {
12   community: CommunityI;
13   communityId: number;
14   communityName: string;
15   moderators: Array<CommunityUser>;
16   admins: Array<UserView>;
17   loading: boolean;
18   posts: Array<Post>;
19   sort: SortType;
20   page: number;
21 }
22
23 export class Community extends Component<any, State> {
24
25   private subscription: Subscription;
26   private postFetcher: any;
27   private emptyState: State = {
28     community: {
29       id: null,
30       name: null,
31       title: null,
32       category_id: null,
33       category_name: null,
34       creator_id: null,
35       creator_name: null,
36       number_of_subscribers: null,
37       number_of_posts: null,
38       number_of_comments: null,
39       published: null,
40       removed: null,
41       nsfw: false,
42       deleted: null,
43     },
44     moderators: [],
45     admins: [],
46     communityId: Number(this.props.match.params.id),
47     communityName: this.props.match.params.name,
48     loading: true,
49     posts: [],
50     sort: this.getSortTypeFromProps(this.props),
51     page: this.getPageFromProps(this.props),
52   }
53
54   getSortTypeFromProps(props: any): SortType {
55     return (props.match.params.sort) ? 
56       routeSortTypeToEnum(props.match.params.sort) : 
57       SortType.Hot;
58   }
59
60   getPageFromProps(props: any): number {
61     return (props.match.params.page) ? Number(props.match.params.page) : 1;
62   }
63
64   constructor(props: any, context: any) {
65     super(props, context);
66
67     this.state = this.emptyState;
68
69     this.subscription = WebSocketService.Instance.subject
70     .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
71     .subscribe(
72       (msg) => this.parseMessage(msg),
73         (err) => console.error(err),
74         () => console.log('complete')
75     );
76
77     if (this.state.communityId) {
78       WebSocketService.Instance.getCommunity(this.state.communityId);
79     } else if (this.state.communityName) {
80       WebSocketService.Instance.getCommunityByName(this.state.communityName);
81     }
82
83     this.keepFetchingPosts();
84   }
85
86   componentWillUnmount() {
87     this.subscription.unsubscribe();
88     clearInterval(this.postFetcher);
89   }
90
91   // Necessary for back button for some reason
92   componentWillReceiveProps(nextProps: any) {
93     if (nextProps.history.action == 'POP') {
94       this.state = this.emptyState;
95       this.state.sort = this.getSortTypeFromProps(nextProps);
96       this.state.page = this.getPageFromProps(nextProps);
97       this.fetchPosts();
98     }
99   }
100
101   render() {
102     return (
103       <div class="container">
104         {this.state.loading ? 
105         <h5><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> : 
106         <div class="row">
107           <div class="col-12 col-md-8">
108             <h5>{this.state.community.title}
109             {this.state.community.removed &&
110               <small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
111             }
112             {this.state.community.nsfw &&
113               <small className="ml-2 text-muted font-italic"><T i18nKey="nsfw">#</T></small>
114             }
115           </h5>
116           {this.selects()}
117           <PostListings posts={this.state.posts} />
118           {this.paginator()}
119           </div>
120           <div class="col-12 col-md-4">
121             <Sidebar 
122               community={this.state.community} 
123               moderators={this.state.moderators} 
124               admins={this.state.admins}
125             />
126           </div>
127         </div>
128         }
129       </div>
130     )
131   }
132
133   selects() {
134     return (
135       <div className="mb-2">
136         <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto">
137           <option disabled><T i18nKey="sort_type">#</T></option>
138           <option value={SortType.Hot}><T i18nKey="hot">#</T></option>
139           <option value={SortType.New}><T i18nKey="new">#</T></option>
140           <option disabled>──────</option>
141           <option value={SortType.TopDay}><T i18nKey="top_day">#</T></option>
142           <option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
143           <option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
144           <option value={SortType.TopYear}><T i18nKey="year">#</T></option>
145           <option value={SortType.TopAll}><T i18nKey="all">#</T></option>
146         </select>
147       </div>
148     )
149   }
150
151   paginator() {
152     return (
153       <div class="my-2">
154         {this.state.page > 1 && 
155           <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
156         }
157         <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
158       </div>
159     );
160   }
161
162   nextPage(i: Community) { 
163     i.state.page++;
164     i.setState(i.state);
165     i.updateUrl();
166     i.fetchPosts();
167     window.scrollTo(0,0);
168   }
169
170   prevPage(i: Community) { 
171     i.state.page--;
172     i.setState(i.state);
173     i.updateUrl();
174     i.fetchPosts();
175     window.scrollTo(0,0);
176   }
177
178   handleSortChange(i: Community, event: any) {
179     i.state.sort = Number(event.target.value);
180     i.state.page = 1;
181     i.setState(i.state);
182     i.updateUrl();
183     i.fetchPosts();
184     window.scrollTo(0,0);
185   }
186
187   updateUrl() {
188     let sortStr = SortType[this.state.sort].toLowerCase();
189     this.props.history.push(`/c/${this.state.community.name}/sort/${sortStr}/page/${this.state.page}`);
190   }
191
192   keepFetchingPosts() {
193     this.fetchPosts();
194     this.postFetcher = setInterval(() => this.fetchPosts(), postRefetchSeconds);
195   }
196
197   fetchPosts() {
198     let getPostsForm: GetPostsForm = {
199       page: this.state.page,
200       limit: fetchLimit,
201       sort: SortType[this.state.sort],
202       type_: ListingType[ListingType.Community],
203       community_id: this.state.community.id,
204     }
205     WebSocketService.Instance.getPosts(getPostsForm);
206   }
207
208   parseMessage(msg: any) {
209     console.log(msg);
210     let op: UserOperation = msgOp(msg);
211     if (msg.error) {
212       alert(i18n.t(msg.error));
213       return;
214     } else if (op == UserOperation.GetCommunity) {
215       let res: GetCommunityResponse = msg;
216       this.state.community = res.community;
217       this.state.moderators = res.moderators;
218       this.state.admins = res.admins;
219       document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`;
220       this.setState(this.state);
221       this.fetchPosts();
222     } else if (op == UserOperation.EditCommunity) {
223       let res: CommunityResponse = msg;
224       this.state.community = res.community;
225       this.setState(this.state);
226     } else if (op == UserOperation.FollowCommunity) {
227       let res: CommunityResponse = msg;
228       this.state.community.subscribed = res.community.subscribed;
229       this.state.community.number_of_subscribers = res.community.number_of_subscribers;
230       this.setState(this.state);
231     } else if (op == UserOperation.GetPosts) {
232       let res: GetPostsResponse = msg;
233       this.state.posts = res.posts;
234       this.state.loading = false;
235       this.setState(this.state);
236     } else if (op == UserOperation.CreatePostLike) {
237       let res: CreatePostLikeResponse = msg;
238       let found = this.state.posts.find(c => c.id == res.post.id);
239       found.my_vote = res.post.my_vote;
240       found.score = res.post.score;
241       found.upvotes = res.post.upvotes;
242       found.downvotes = res.post.downvotes;
243       this.setState(this.state);
244     }
245   }
246 }
247