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 (msg.reconnect) {
318 } else if (res.op == UserOperation.GetReplies) {
319 let data = res.data as GetRepliesResponse;
320 this.state.replies = data.replies;
321 this.sendUnreadCount();
322 window.scrollTo(0, 0);
323 this.setState(this.state);
324 } else if (res.op == UserOperation.GetUserMentions) {
325 let data = res.data as GetUserMentionsResponse;
326 this.state.mentions = data.mentions;
327 this.sendUnreadCount();
328 window.scrollTo(0, 0);
329 this.setState(this.state);
330 } else if (res.op == UserOperation.GetPrivateMessages) {
331 let data = res.data as PrivateMessagesResponse;
332 this.state.messages = data.messages;
333 this.sendUnreadCount();
334 window.scrollTo(0, 0);
335 this.setState(this.state);
336 } else if (res.op == UserOperation.EditPrivateMessage) {
337 let data = res.data as PrivateMessageResponse;
338 let found: PrivateMessageI = this.state.messages.find(
339 m => m.id === data.message.id
341 found.content = data.message.content;
342 found.updated = data.message.updated;
343 found.deleted = data.message.deleted;
344 // If youre in the unread view, just remove it from the list
345 if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) {
346 this.state.messages = this.state.messages.filter(
347 r => r.id !== data.message.id
350 let found = this.state.messages.find(c => c.id == data.message.id);
351 found.read = data.message.read;
353 this.sendUnreadCount();
354 window.scrollTo(0, 0);
355 this.setState(this.state);
356 } else if (res.op == UserOperation.MarkAllAsRead) {
357 this.state.replies = [];
358 this.state.mentions = [];
359 this.state.messages = [];
360 this.sendUnreadCount();
361 window.scrollTo(0, 0);
362 this.setState(this.state);
363 } else if (res.op == UserOperation.EditComment) {
364 let data = res.data as CommentResponse;
366 let found = this.state.replies.find(c => c.id == data.comment.id);
367 found.content = data.comment.content;
368 found.updated = data.comment.updated;
369 found.removed = data.comment.removed;
370 found.deleted = data.comment.deleted;
371 found.upvotes = data.comment.upvotes;
372 found.downvotes = data.comment.downvotes;
373 found.score = data.comment.score;
375 // If youre in the unread view, just remove it from the list
376 if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) {
377 this.state.replies = this.state.replies.filter(
378 r => r.id !== data.comment.id
381 let found = this.state.replies.find(c => c.id == data.comment.id);
382 found.read = data.comment.read;
384 this.sendUnreadCount();
385 this.setState(this.state);
386 } else if (res.op == UserOperation.EditUserMention) {
387 let data = res.data as UserMentionResponse;
389 let found = this.state.mentions.find(c => c.id == data.mention.id);
390 found.content = data.mention.content;
391 found.updated = data.mention.updated;
392 found.removed = data.mention.removed;
393 found.deleted = data.mention.deleted;
394 found.upvotes = data.mention.upvotes;
395 found.downvotes = data.mention.downvotes;
396 found.score = data.mention.score;
398 // If youre in the unread view, just remove it from the list
399 if (this.state.unreadOrAll == UnreadOrAll.Unread && data.mention.read) {
400 this.state.mentions = this.state.mentions.filter(
401 r => r.id !== data.mention.id
404 let found = this.state.mentions.find(c => c.id == data.mention.id);
405 found.read = data.mention.read;
407 this.sendUnreadCount();
408 this.setState(this.state);
409 } else if (res.op == UserOperation.CreateComment) {
410 let data = res.data as CommentResponse;
412 if (data.recipient_ids.includes(UserService.Instance.user.id)) {
413 this.state.replies.unshift(data.comment);
414 this.setState(this.state);
415 } else if (data.comment.creator_id == UserService.Instance.user.id) {
416 toast(i18n.t('reply_sent'));
418 this.setState(this.state);
419 } else if (res.op == UserOperation.CreatePrivateMessage) {
420 let data = res.data as PrivateMessageResponse;
422 if (data.message.recipient_id == UserService.Instance.user.id) {
423 this.state.messages.unshift(data.message);
424 this.setState(this.state);
425 } else if (data.message.creator_id == UserService.Instance.user.id) {
426 toast(i18n.t('message_sent'));
428 this.setState(this.state);
429 } else if (res.op == UserOperation.SaveComment) {
430 let data = res.data as CommentResponse;
431 let found = this.state.replies.find(c => c.id == data.comment.id);
432 found.saved = data.comment.saved;
433 this.setState(this.state);
434 } else if (res.op == UserOperation.CreateCommentLike) {
435 let data = res.data as CommentResponse;
436 let found: Comment = this.state.replies.find(
437 c => c.id === data.comment.id
439 found.score = data.comment.score;
440 found.upvotes = data.comment.upvotes;
441 found.downvotes = data.comment.downvotes;
442 if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote;
443 this.setState(this.state);
449 this.state.replies.filter(r => !r.read).length +
450 this.state.mentions.filter(r => !r.read).length +
451 this.state.messages.filter(
452 r => !r.read && r.creator_id !== UserService.Instance.user.id
454 UserService.Instance.sub.next({
455 user: UserService.Instance.user,