1 import { Component, linkEvent } from 'inferno';
2 import { Subscription } from 'rxjs';
3 import { DataType } from '../interfaces';
15 AddModToCommunityResponse,
16 BanFromCommunityResponse,
21 WebSocketJsonResponse,
24 ListCategoriesResponse,
25 } from 'lemmy-js-client';
26 import { UserService, WebSocketService } from '../services';
27 import { PostListings } from './post-listings';
28 import { CommentNodes } from './comment-nodes';
29 import { HtmlTags } from './html-tags';
30 import { SortSelect } from './sort-select';
31 import { DataTypeSelect } from './data-type-select';
32 import { Sidebar } from './sidebar';
33 import { CommunityLink } from './community-link';
34 import { BannerIconHeader } from './banner-icon-header';
45 createPostLikeFindRes,
56 import { i18n } from '../i18next';
59 communityRes: GetCommunityResponse;
60 siteRes: GetSiteResponse;
62 communityName: string;
69 categories: Category[];
72 interface CommunityProps {
84 export class Community extends Component<any, State> {
85 private isoData = setIsoData(this.context);
86 private subscription: Subscription;
87 private emptyState: State = {
88 communityRes: undefined,
89 communityId: Number(this.props.match.params.id),
90 communityName: this.props.match.params.name,
94 dataType: getDataTypeFromProps(this.props),
95 sort: getSortTypeFromProps(this.props),
96 page: getPageFromProps(this.props),
97 siteRes: this.isoData.site,
101 constructor(props: any, context: any) {
102 super(props, context);
104 this.state = this.emptyState;
105 this.handleSortChange = this.handleSortChange.bind(this);
106 this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
108 this.parseMessage = this.parseMessage.bind(this);
109 this.subscription = wsSubscribe(this.parseMessage);
111 // Only fetch the data if coming from another route
112 if (this.isoData.path == this.context.router.route.match.url) {
113 this.state.communityRes = this.isoData.routeData[0];
114 if (this.state.dataType == DataType.Post) {
115 this.state.posts = this.isoData.routeData[1].posts;
117 this.state.comments = this.isoData.routeData[1].comments;
119 this.state.categories = this.isoData.routeData[2].categories;
120 this.state.loading = false;
122 this.fetchCommunity();
124 WebSocketService.Instance.listCategories();
130 let form: GetCommunityForm = {
131 id: this.state.communityId ? this.state.communityId : null,
132 name: this.state.communityName ? this.state.communityName : null,
134 WebSocketService.Instance.getCommunity(form);
137 componentWillUnmount() {
139 this.subscription.unsubscribe();
143 static getDerivedStateFromProps(props: any): CommunityProps {
145 dataType: getDataTypeFromProps(props),
146 sort: getSortTypeFromProps(props),
147 page: getPageFromProps(props),
151 static fetchInitialData(auth: string, path: string): Promise<any>[] {
152 let pathSplit = path.split('/');
153 let promises: Promise<any>[] = [];
155 // It can be /c/main, or /c/1
156 let idOrName = pathSplit[2];
159 if (isNaN(Number(idOrName))) {
162 id = Number(idOrName);
165 let communityForm: GetCommunityForm = id ? { id } : { name: name_ };
166 setAuth(communityForm, auth);
167 promises.push(lemmyHttp.getCommunity(communityForm));
169 let dataType: DataType = pathSplit[4]
170 ? DataType[pathSplit[4]]
173 let sort: SortType = pathSplit[6]
174 ? SortType[pathSplit[6]]
175 : UserService.Instance.user
176 ? Object.values(SortType)[UserService.Instance.user.default_sort_type]
179 let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
181 if (dataType == DataType.Post) {
182 let getPostsForm: GetPostsForm = {
186 type_: ListingType.Community,
188 this.setIdOrName(getPostsForm, id, name_);
189 setAuth(getPostsForm, auth);
190 promises.push(lemmyHttp.getPosts(getPostsForm));
192 let getCommentsForm: GetCommentsForm = {
196 type_: ListingType.Community,
198 this.setIdOrName(getCommentsForm, id, name_);
199 setAuth(getCommentsForm, auth);
200 promises.push(lemmyHttp.getComments(getCommentsForm));
203 promises.push(lemmyHttp.listCategories());
208 static setIdOrName(obj: any, id: number, name_: string) {
210 obj.community_id = id;
212 obj.community_name = name_;
216 componentDidUpdate(_: any, lastState: State) {
218 lastState.dataType !== this.state.dataType ||
219 lastState.sort !== this.state.sort ||
220 lastState.page !== this.state.page
222 this.setState({ loading: true });
227 get documentTitle(): string {
228 return `${this.state.communityRes.community.title} - ${this.state.siteRes.site.name}`;
233 <div class="container">
234 {this.state.loading ? (
236 <svg class="icon icon-spinner spin">
237 <use xlinkHref="#icon-spinner"></use>
242 <div class="col-12 col-md-8">
244 title={this.documentTitle}
245 path={this.context.router.route.match.url}
246 description={this.state.communityRes.community.title}
247 image={this.state.communityRes.community.icon}
249 {this.communityInfo()}
254 <div class="col-12 col-md-4">
256 community={this.state.communityRes.community}
257 moderators={this.state.communityRes.moderators}
258 admins={this.state.siteRes.admins}
259 online={this.state.communityRes.online}
260 enableNsfw={this.state.siteRes.site.enable_nsfw}
261 categories={this.state.categories}
271 return this.state.dataType == DataType.Post ? (
273 posts={this.state.posts}
275 sort={this.state.sort}
276 enableDownvotes={this.state.siteRes.site.enable_downvotes}
277 enableNsfw={this.state.siteRes.site.enable_nsfw}
281 nodes={commentsToFlatNodes(this.state.comments)}
283 sortType={this.state.sort}
285 enableDownvotes={this.state.siteRes.site.enable_downvotes}
294 banner={this.state.communityRes.community.banner}
295 icon={this.state.communityRes.community.icon}
297 <h5 class="mb-0">{this.state.communityRes.community.title}</h5>
299 community={this.state.communityRes.community}
315 type_={this.state.dataType}
316 onChange={this.handleDataTypeChange}
320 <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
323 href={`/feeds/c/${this.state.communityName}.xml?sort=${this.state.sort}`}
328 <svg class="icon text-muted small">
329 <use xlinkHref="#icon-rss">#</use>
339 {this.state.page > 1 && (
341 class="btn btn-secondary mr-1"
342 onClick={linkEvent(this, this.prevPage)}
347 {this.state.posts.length > 0 && (
349 class="btn btn-secondary"
350 onClick={linkEvent(this, this.nextPage)}
359 nextPage(i: Community) {
360 i.updateUrl({ page: i.state.page + 1 });
361 window.scrollTo(0, 0);
364 prevPage(i: Community) {
365 i.updateUrl({ page: i.state.page - 1 });
366 window.scrollTo(0, 0);
369 handleSortChange(val: SortType) {
370 this.updateUrl({ sort: val, page: 1 });
371 window.scrollTo(0, 0);
374 handleDataTypeChange(val: DataType) {
375 this.updateUrl({ dataType: DataType[val], page: 1 });
376 window.scrollTo(0, 0);
379 updateUrl(paramUpdates: UrlParams) {
380 const dataTypeStr = paramUpdates.dataType || DataType[this.state.dataType];
381 const sortStr = paramUpdates.sort || this.state.sort;
382 const page = paramUpdates.page || this.state.page;
383 this.props.history.push(
384 `/c/${this.state.communityRes.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${page}`
389 if (this.state.dataType == DataType.Post) {
390 let getPostsForm: GetPostsForm = {
391 page: this.state.page,
393 sort: this.state.sort,
394 type_: ListingType.Community,
395 community_id: this.state.communityId,
396 community_name: this.state.communityName,
398 WebSocketService.Instance.getPosts(getPostsForm);
400 let getCommentsForm: GetCommentsForm = {
401 page: this.state.page,
403 sort: this.state.sort,
404 type_: ListingType.Community,
405 community_id: this.state.communityId,
406 community_name: this.state.communityName,
408 WebSocketService.Instance.getComments(getCommentsForm);
412 parseMessage(msg: WebSocketJsonResponse) {
414 let res = wsJsonToRes(msg);
416 toast(i18n.t(msg.error), 'danger');
417 this.context.router.history.push('/');
419 } else if (msg.reconnect) {
421 } else if (res.op == UserOperation.GetCommunity) {
422 let data = res.data as GetCommunityResponse;
423 this.state.communityRes = data;
424 if (this.state.posts.length || this.state.comments.length) {
425 this.state.loading = false;
427 this.setState(this.state);
428 WebSocketService.Instance.communityJoin({
429 community_id: data.community.id,
432 res.op == UserOperation.EditCommunity ||
433 res.op == UserOperation.DeleteCommunity ||
434 res.op == UserOperation.RemoveCommunity
436 let data = res.data as CommunityResponse;
437 this.state.communityRes.community = data.community;
438 this.setState(this.state);
439 } else if (res.op == UserOperation.FollowCommunity) {
440 let data = res.data as CommunityResponse;
441 this.state.communityRes.community.subscribed = data.community.subscribed;
442 this.state.communityRes.community.number_of_subscribers =
443 data.community.number_of_subscribers;
444 this.setState(this.state);
445 } else if (res.op == UserOperation.GetPosts) {
446 let data = res.data as GetPostsResponse;
447 this.state.posts = data.posts;
449 if (this.state.communityRes) {
450 this.state.loading = false;
452 this.setState(this.state);
455 res.op == UserOperation.EditPost ||
456 res.op == UserOperation.DeletePost ||
457 res.op == UserOperation.RemovePost ||
458 res.op == UserOperation.LockPost ||
459 res.op == UserOperation.StickyPost
461 let data = res.data as PostResponse;
462 editPostFindRes(data, this.state.posts);
463 this.setState(this.state);
464 } else if (res.op == UserOperation.CreatePost) {
465 let data = res.data as PostResponse;
466 this.state.posts.unshift(data.post);
467 notifyPost(data.post, this.context.router);
468 this.setState(this.state);
469 } else if (res.op == UserOperation.CreatePostLike) {
470 let data = res.data as PostResponse;
471 createPostLikeFindRes(data, this.state.posts);
472 this.setState(this.state);
473 } else if (res.op == UserOperation.AddModToCommunity) {
474 let data = res.data as AddModToCommunityResponse;
475 this.state.communityRes.moderators = data.moderators;
476 this.setState(this.state);
477 } else if (res.op == UserOperation.BanFromCommunity) {
478 let data = res.data as BanFromCommunityResponse;
481 .filter(p => p.creator_id == data.user.id)
482 .forEach(p => (p.banned = data.banned));
484 this.setState(this.state);
485 } else if (res.op == UserOperation.GetComments) {
486 let data = res.data as GetCommentsResponse;
487 this.state.comments = data.comments;
488 if (this.state.communityRes) {
489 this.state.loading = false;
491 this.setState(this.state);
493 res.op == UserOperation.EditComment ||
494 res.op == UserOperation.DeleteComment ||
495 res.op == UserOperation.RemoveComment
497 let data = res.data as CommentResponse;
498 editCommentRes(data, this.state.comments);
499 this.setState(this.state);
500 } else if (res.op == UserOperation.CreateComment) {
501 let data = res.data as CommentResponse;
503 // Necessary since it might be a user reply
504 if (data.recipient_ids.length == 0) {
505 this.state.comments.unshift(data.comment);
506 this.setState(this.state);
508 } else if (res.op == UserOperation.SaveComment) {
509 let data = res.data as CommentResponse;
510 saveCommentRes(data, this.state.comments);
511 this.setState(this.state);
512 } else if (res.op == UserOperation.CreateCommentLike) {
513 let data = res.data as CommentResponse;
514 createCommentLikeRes(data, this.state.comments);
515 this.setState(this.state);
516 } else if (res.op == UserOperation.ListCategories) {
517 let data = res.data as ListCategoriesResponse;
518 this.state.categories = data.categories;
519 this.setState(this.state);