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';
12 GetUserMentionsResponse,
15 WebSocketJsonResponse,
16 PrivateMessage as PrivateMessageI,
17 GetPrivateMessagesForm,
18 PrivateMessagesResponse,
19 PrivateMessageResponse,
20 } from '../interfaces';
21 import { WebSocketService, UserService } from '../services';
22 import { wsJsonToRes, fetchLimit, isCommentType, toast } from '../utils';
23 import { CommentNodes } from './comment-nodes';
24 import { PrivateMessage } from './private-message';
25 import { SortSelect } from './sort-select';
26 import { i18n } from '../i18next';
27 import { T } from 'inferno-i18next';
41 type ReplyType = Comment | PrivateMessageI;
43 interface InboxState {
44 unreadOrAll: UnreadOrAll;
45 unreadType: UnreadType;
46 replies: Array<Comment>;
47 mentions: Array<Comment>;
48 messages: Array<PrivateMessageI>;
53 export class Inbox extends Component<any, InboxState> {
54 private subscription: Subscription;
55 private emptyState: InboxState = {
56 unreadOrAll: UnreadOrAll.Unread,
57 unreadType: UnreadType.All,
65 constructor(props: any, context: any) {
66 super(props, context);
68 this.state = this.emptyState;
69 this.handleSortChange = this.handleSortChange.bind(this);
71 this.subscription = WebSocketService.Instance.subject
72 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
74 msg => this.parseMessage(msg),
75 err => console.error(err),
76 () => console.log('complete')
82 componentWillUnmount() {
83 this.subscription.unsubscribe();
87 document.title = `/u/${UserService.Instance.user.username} ${i18n.t(
89 )} - ${WebSocketService.Instance.site.name}`;
93 let user = UserService.Instance.user;
95 <div class="container">
102 interpolation={{ user: user.username }}
104 #<Link to={`/u/${user.username}`}>#</Link>
108 href={`/feeds/inbox/${UserService.Instance.auth}.xml`}
111 <svg class="icon mx-2 text-muted small">
112 <use xlinkHref="#icon-rss">#</use>
117 {this.state.replies.length +
118 this.state.mentions.length +
119 this.state.messages.length >
121 this.state.unreadOrAll == UnreadOrAll.Unread && (
122 <ul class="list-inline mb-1 text-muted small font-weight-bold">
123 <li className="list-inline-item">
124 <span class="pointer" onClick={this.markAllAsRead}>
125 {i18n.t('mark_all_as_read')}
131 {this.state.unreadType == UnreadType.All && this.all()}
132 {this.state.unreadType == UnreadType.Replies && this.replies()}
133 {this.state.unreadType == UnreadType.Mentions && this.mentions()}
134 {this.state.unreadType == UnreadType.Messages && this.messages()}
144 <div className="mb-2">
146 value={this.state.unreadOrAll}
147 onChange={linkEvent(this, this.handleUnreadOrAllChange)}
148 class="custom-select custom-select-sm w-auto mr-2"
150 <option disabled>{i18n.t('type')}</option>
151 <option value={UnreadOrAll.Unread}>{i18n.t('unread')}</option>
152 <option value={UnreadOrAll.All}>{i18n.t('all')}</option>
155 value={this.state.unreadType}
156 onChange={linkEvent(this, this.handleUnreadTypeChange)}
157 class="custom-select custom-select-sm w-auto mr-2"
159 <option disabled>{i18n.t('type')}</option>
160 <option value={UnreadType.All}>{i18n.t('all')}</option>
161 <option value={UnreadType.Replies}>{i18n.t('replies')}</option>
162 <option value={UnreadType.Mentions}>{i18n.t('mentions')}</option>
163 <option value={UnreadType.Messages}>{i18n.t('messages')}</option>
166 sort={this.state.sort}
167 onChange={this.handleSortChange}
175 let combined: Array<ReplyType> = [];
177 combined.push(...this.state.replies);
178 combined.push(...this.state.mentions);
179 combined.push(...this.state.messages);
182 combined.sort((a, b) => b.published.localeCompare(a.published));
188 <CommentNodes nodes={[{ comment: i }]} noIndent markable />
190 <PrivateMessage privateMessage={i} />
200 {this.state.replies.map(reply => (
201 <CommentNodes nodes={[{ comment: reply }]} noIndent markable />
210 {this.state.mentions.map(mention => (
211 <CommentNodes nodes={[{ comment: mention }]} noIndent markable />
220 {this.state.messages.map(message => (
221 <PrivateMessage privateMessage={message} />
230 {this.state.page > 1 && (
232 class="btn btn-sm btn-secondary mr-1"
233 onClick={linkEvent(this, this.prevPage)}
239 class="btn btn-sm btn-secondary"
240 onClick={linkEvent(this, this.nextPage)}
260 handleUnreadOrAllChange(i: Inbox, event: any) {
261 i.state.unreadOrAll = Number(event.target.value);
267 handleUnreadTypeChange(i: Inbox, event: any) {
268 i.state.unreadType = Number(event.target.value);
275 let repliesForm: GetRepliesForm = {
276 sort: SortType[this.state.sort],
277 unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
278 page: this.state.page,
281 WebSocketService.Instance.getReplies(repliesForm);
283 let userMentionsForm: GetUserMentionsForm = {
284 sort: SortType[this.state.sort],
285 unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
286 page: this.state.page,
289 WebSocketService.Instance.getUserMentions(userMentionsForm);
291 let privateMessagesForm: GetPrivateMessagesForm = {
292 unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
293 page: this.state.page,
296 WebSocketService.Instance.getPrivateMessages(privateMessagesForm);
299 handleSortChange(val: SortType) {
300 this.state.sort = val;
302 this.setState(this.state);
307 WebSocketService.Instance.markAllAsRead();
310 parseMessage(msg: WebSocketJsonResponse) {
312 let res = wsJsonToRes(msg);
314 toast(i18n.t(msg.error), 'danger');
316 } else if (res.op == UserOperation.GetReplies) {
317 let data = res.data as GetRepliesResponse;
318 this.state.replies = data.replies;
319 this.sendUnreadCount();
320 window.scrollTo(0, 0);
321 this.setState(this.state);
322 } else if (res.op == UserOperation.GetUserMentions) {
323 let data = res.data as GetUserMentionsResponse;
324 this.state.mentions = data.mentions;
325 this.sendUnreadCount();
326 window.scrollTo(0, 0);
327 this.setState(this.state);
328 } else if (res.op == UserOperation.GetPrivateMessages) {
329 let data = res.data as PrivateMessagesResponse;
330 this.state.messages = data.messages;
331 this.sendUnreadCount();
332 window.scrollTo(0, 0);
333 this.setState(this.state);
334 } else if (res.op == UserOperation.EditPrivateMessage) {
335 let data = res.data as PrivateMessageResponse;
336 let found: PrivateMessageI = this.state.messages.find(
337 m => m.id === data.message.id
339 found.content = data.message.content;
340 found.updated = data.message.updated;
341 found.deleted = data.message.deleted;
342 // If youre in the unread view, just remove it from the list
343 if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) {
344 this.state.messages = this.state.messages.filter(
345 r => r.id !== data.message.id
348 let found = this.state.messages.find(c => c.id == data.message.id);
349 found.read = data.message.read;
351 this.sendUnreadCount();
352 window.scrollTo(0, 0);
353 this.setState(this.state);
354 } else if (res.op == UserOperation.MarkAllAsRead) {
355 this.state.replies = [];
356 this.state.mentions = [];
357 this.state.messages = [];
358 this.sendUnreadCount();
359 window.scrollTo(0, 0);
360 this.setState(this.state);
361 } else if (res.op == UserOperation.EditComment) {
362 let data = res.data as CommentResponse;
364 let found = this.state.replies.find(c => c.id == data.comment.id);
365 found.content = data.comment.content;
366 found.updated = data.comment.updated;
367 found.removed = data.comment.removed;
368 found.deleted = data.comment.deleted;
369 found.upvotes = data.comment.upvotes;
370 found.downvotes = data.comment.downvotes;
371 found.score = data.comment.score;
373 // If youre in the unread view, just remove it from the list
374 if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) {
375 this.state.replies = this.state.replies.filter(
376 r => r.id !== data.comment.id
379 let found = this.state.replies.find(c => c.id == data.comment.id);
380 found.read = data.comment.read;
382 this.sendUnreadCount();
383 this.setState(this.state);
384 } else if (res.op == UserOperation.EditUserMention) {
385 let data = res.data as UserMentionResponse;
387 let found = this.state.mentions.find(c => c.id == data.mention.id);
388 found.content = data.mention.content;
389 found.updated = data.mention.updated;
390 found.removed = data.mention.removed;
391 found.deleted = data.mention.deleted;
392 found.upvotes = data.mention.upvotes;
393 found.downvotes = data.mention.downvotes;
394 found.score = data.mention.score;
396 // If youre in the unread view, just remove it from the list
397 if (this.state.unreadOrAll == UnreadOrAll.Unread && data.mention.read) {
398 this.state.mentions = this.state.mentions.filter(
399 r => r.id !== data.mention.id
402 let found = this.state.mentions.find(c => c.id == data.mention.id);
403 found.read = data.mention.read;
405 this.sendUnreadCount();
406 this.setState(this.state);
407 } else if (res.op == UserOperation.CreateComment) {
408 let data = res.data as CommentResponse;
410 if (data.recipient_ids.includes(UserService.Instance.user.id)) {
411 this.state.replies.unshift(data.comment);
412 this.setState(this.state);
413 } else if (data.comment.creator_id == UserService.Instance.user.id) {
414 toast(i18n.t('reply_sent'));
416 this.setState(this.state);
417 } else if (res.op == UserOperation.CreatePrivateMessage) {
418 let data = res.data as PrivateMessageResponse;
420 if (data.message.recipient_id == UserService.Instance.user.id) {
421 this.state.messages.unshift(data.message);
422 this.setState(this.state);
423 } else if (data.message.creator_id == UserService.Instance.user.id) {
424 toast(i18n.t('message_sent'));
426 this.setState(this.state);
427 } else if (res.op == UserOperation.SaveComment) {
428 let data = res.data as CommentResponse;
429 let found = this.state.replies.find(c => c.id == data.comment.id);
430 found.saved = data.comment.saved;
431 this.setState(this.state);
432 } else if (res.op == UserOperation.CreateCommentLike) {
433 let data = res.data as CommentResponse;
434 let found: Comment = this.state.replies.find(
435 c => c.id === data.comment.id
437 found.score = data.comment.score;
438 found.upvotes = data.comment.upvotes;
439 found.downvotes = data.comment.downvotes;
440 if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote;
441 this.setState(this.state);
447 this.state.replies.filter(r => !r.read).length +
448 this.state.mentions.filter(r => !r.read).length +
449 this.state.messages.filter(
450 r => !r.read && r.creator_id !== UserService.Instance.user.id
452 UserService.Instance.sub.next({
453 user: UserService.Instance.user,