]> Untitled Git - lemmy.git/blob - ui/src/components/user-details.tsx
ui.components: split user component up to fix duplicate requests
[lemmy.git] / ui / src / components / user-details.tsx
1 import { Component } from 'inferno';
2 import { WebSocketService, UserService } from '../services';
3 import { Subscription } from 'rxjs';
4 import { retryWhen, delay, take, last } from 'rxjs/operators';
5 import { i18n } from '../i18next';
6 import {
7   UserOperation,
8   Post,
9   Comment,
10   CommunityUser,
11   SortType,
12   UserDetailsResponse,
13   UserView,
14   WebSocketJsonResponse,
15   UserDetailsView,
16   CommentResponse,
17   BanUserResponse,
18   PostResponse,
19   AddAdminResponse,
20 } from '../interfaces';
21 import {
22   wsJsonToRes,
23   toast,
24   commentsToFlatNodes,
25   setupTippy,
26   editCommentRes,
27   saveCommentRes,
28   createCommentLikeRes,
29   createPostLikeFindRes,
30 } from '../utils';
31 import { PostListing } from './post-listing';
32 import { CommentNodes } from './comment-nodes';
33
34 interface UserDetailsProps {
35   username?: string;
36   user_id?: number;
37   page: number;
38   limit: number;
39   sort: string;
40   enableDownvotes: boolean;
41   enableNsfw: boolean;
42   view: UserDetailsView;
43 }
44
45 interface UserDetailsState {
46   follows: Array<CommunityUser>;
47   moderates: Array<CommunityUser>;
48   comments: Array<Comment>;
49   posts: Array<Post>;
50   saved?: Array<Post>;
51   admins: Array<UserView>;
52 }
53
54 export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
55   private subscription: Subscription;
56   constructor(props: any, context: any) {
57     super(props, context);
58
59     this.state = {
60       follows: [],
61       moderates: [],
62       comments: [],
63       posts: [],
64       saved: [],
65       admins: [],
66     };
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
77   componentWillUnmount() {
78     this.subscription.unsubscribe();
79   }
80
81   componentDidMount() {
82     this.fetchUserData();
83   }
84
85   componentDidUpdate(lastProps: UserDetailsProps) {
86     for (const key of Object.keys(lastProps)) {
87       if (lastProps[key] !== this.props[key]) {
88         this.fetchUserData();
89         break;
90       }
91     }
92     setupTippy();
93   }
94
95   fetchUserData() {
96     WebSocketService.Instance.getUserDetails({
97       user_id: this.props.user_id,
98       username: this.props.username,
99       sort: this.props.sort,
100       saved_only: this.props.view === UserDetailsView.Saved,
101       page: this.props.page,
102       limit: this.props.limit,
103     });
104   }
105
106   render() {
107     return this.viewSelector(this.props.view);
108   }
109
110   viewSelector(view: UserDetailsView) {
111     if (view === UserDetailsView.Overview || view === UserDetailsView.Saved) {
112       return this.overview();
113     }
114     if (view === UserDetailsView.Comments) {
115       return this.comments();
116     }
117     if (view === UserDetailsView.Posts) {
118       return this.posts();
119     }
120   }
121
122   overview() {
123     const comments = this.state.comments.map((c: Comment) => {
124       return { type: 'comments', data: c };
125     });
126     const posts = this.state.posts.map((p: Post) => {
127       return { type: 'posts', data: p };
128     });
129
130     const combined: Array<{ type: string; data: Comment | Post }> = [
131       ...comments,
132       ...posts,
133     ];
134
135     // Sort it
136     if (SortType[this.props.sort] === SortType.New) {
137       combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
138     } else {
139       combined.sort((a, b) => b.data.score - a.data.score);
140     }
141
142     return (
143       <div>
144         {combined.map(i => (
145           <div>
146             {i.type === 'posts' ? (
147               <PostListing
148                 post={i.data as Post}
149                 admins={this.state.admins}
150                 showCommunity
151                 enableDownvotes={this.props.enableDownvotes}
152                 enableNsfw={this.props.enableNsfw}
153               />
154             ) : (
155               <CommentNodes
156                 nodes={[{ comment: i.data as Comment }]}
157                 admins={this.state.admins}
158                 noIndent
159                 showContext
160                 enableDownvotes={this.props.enableDownvotes}
161               />
162             )}
163           </div>
164         ))}
165       </div>
166     );
167   }
168
169   comments() {
170     return (
171       <div>
172         <CommentNodes
173           nodes={commentsToFlatNodes(this.state.comments)}
174           admins={this.state.admins}
175           noIndent
176           showContext
177           enableDownvotes={this.props.enableDownvotes}
178         />
179       </div>
180     );
181   }
182
183   posts() {
184     return (
185       <div>
186         {this.state.posts.map(post => (
187           <PostListing
188             post={post}
189             admins={this.state.admins}
190             showCommunity
191             enableDownvotes={this.props.enableDownvotes}
192             enableNsfw={this.props.enableNsfw}
193           />
194         ))}
195       </div>
196     );
197   }
198
199   parseMessage(msg: WebSocketJsonResponse) {
200     const res = wsJsonToRes(msg);
201
202     if (msg.error) {
203       toast(i18n.t(msg.error), 'danger');
204       if (msg.error == 'couldnt_find_that_username_or_email') {
205         this.context.router.history.push('/');
206       }
207       return;
208     } else if (msg.reconnect) {
209       this.fetchUserData();
210     } else if (res.op == UserOperation.GetUserDetails) {
211       const data = res.data as UserDetailsResponse;
212       this.setState({
213         comments: data.comments,
214         follows: data.follows,
215         moderates: data.moderates,
216         posts: data.posts,
217         admins: data.admins,
218       });
219     } else if (res.op == UserOperation.CreateCommentLike) {
220       const data = res.data as CommentResponse;
221       createCommentLikeRes(data, this.state.comments);
222       this.setState({
223         comments: this.state.comments,
224       });
225     } else if (res.op == UserOperation.EditComment) {
226       const data = res.data as CommentResponse;
227       editCommentRes(data, this.state.comments);
228       this.setState({
229         comments: this.state.comments,
230       });
231     } else if (res.op == UserOperation.CreateComment) {
232       const data = res.data as CommentResponse;
233       if (
234         UserService.Instance.user &&
235         data.comment.creator_id == UserService.Instance.user.id
236       ) {
237         toast(i18n.t('reply_sent'));
238       }
239     } else if (res.op == UserOperation.SaveComment) {
240       const data = res.data as CommentResponse;
241       saveCommentRes(data, this.state.comments);
242       this.setState({
243         comments: this.state.comments,
244       });
245     } else if (res.op == UserOperation.CreatePostLike) {
246       const data = res.data as PostResponse;
247       createPostLikeFindRes(data, this.state.posts);
248       this.setState({
249         posts: this.state.posts,
250       });
251     } else if (res.op == UserOperation.BanUser) {
252       const data = res.data as BanUserResponse;
253       this.state.comments
254         .filter(c => c.creator_id == data.user.id)
255         .forEach(c => (c.banned = data.banned));
256       this.state.posts
257         .filter(c => c.creator_id == data.user.id)
258         .forEach(c => (c.banned = data.banned));
259       this.setState({
260         posts: this.state.posts,
261         comments: this.state.comments,
262       });
263     } else if (res.op == UserOperation.AddAdmin) {
264       const data = res.data as AddAdminResponse;
265       this.setState({
266         admins: data.admins,
267       });
268     }
269   }
270 }