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