]> Untitled Git - lemmy.git/blob - ui/src/components/inbox.tsx
Adding emoji support.
[lemmy.git] / ui / src / components / inbox.tsx
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';
5 import { UserOperation, Comment, SortType, GetRepliesForm, GetRepliesResponse, CommentResponse } from '../interfaces';
6 import { WebSocketService, UserService } from '../services';
7 import { msgOp } from '../utils';
8 import { CommentNodes } from './comment-nodes';
9 import { i18n } from '../i18next';
10 import { T } from 'inferno-i18next';
11
12 enum UnreadType {
13   Unread, All
14 }
15
16 interface InboxState {
17   unreadType: UnreadType;
18   replies: Array<Comment>;
19   sort: SortType;
20   page: number;
21 }
22
23 export class Inbox extends Component<any, InboxState> {
24
25   private subscription: Subscription;
26   private emptyState: InboxState = {
27     unreadType: UnreadType.Unread,
28     replies: [],
29     sort: SortType.New,
30     page: 1,
31   }
32
33   constructor(props: any, context: any) {
34     super(props, context);
35
36     this.state = this.emptyState;
37
38     this.subscription = WebSocketService.Instance.subject
39     .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
40     .subscribe(
41       (msg) => this.parseMessage(msg),
42         (err) => console.error(err),
43         () => console.log('complete')
44     );
45
46     this.refetch();
47   }
48
49   componentWillUnmount() {
50     this.subscription.unsubscribe();
51   }
52
53   componentDidMount() {
54     document.title = `/u/${UserService.Instance.user.username} ${i18n.t('inbox')} - ${WebSocketService.Instance.site.name}`;
55   }
56
57   render() {
58     let user = UserService.Instance.user;
59     return (
60       <div class="container">
61         <div class="row">
62           <div class="col-12">
63             <h5 class="mb-0">
64               <span><T i18nKey="inbox_for" interpolation={{user: user.username}}>#<Link to={`/u/${user.username}`}>#</Link></T></span>
65             </h5>
66             {this.state.replies.length > 0 && this.state.unreadType == UnreadType.Unread &&
67               <ul class="list-inline mb-1 text-muted small font-weight-bold">
68                 <li className="list-inline-item">
69                   <span class="pointer" onClick={this.markAllAsRead}><T i18nKey="mark_all_as_read">#</T></span>
70                 </li>
71               </ul>
72             }
73             {this.selects()}
74             {this.replies()}
75             {this.paginator()}
76           </div>
77         </div>
78       </div>
79     )
80   }
81
82   selects() {
83     return (
84       <div className="mb-2">
85         <select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select custom-select-sm w-auto">
86           <option disabled><T i18nKey="type">#</T></option>
87           <option value={UnreadType.Unread}><T i18nKey="unread">#</T></option>
88           <option value={UnreadType.All}><T i18nKey="all">#</T></option>
89         </select>
90         <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
91           <option disabled><T i18nKey="sort_type">#</T></option>
92           <option value={SortType.New}><T i18nKey="new">#</T></option>
93           <option value={SortType.TopDay}><T i18nKey="top_day">#</T></option>
94           <option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
95           <option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
96           <option value={SortType.TopYear}><T i18nKey="year">#</T></option>
97           <option value={SortType.TopAll}><T i18nKey="all">#</T></option>
98         </select>
99       </div>
100     )
101
102   }
103
104   replies() {
105     return (
106       <div>
107         {this.state.replies.map(reply => 
108           <CommentNodes nodes={[{comment: reply}]} noIndent markable />
109         )}
110       </div>
111     );
112   }
113
114   paginator() {
115     return (
116       <div class="mt-2">
117         {this.state.page > 1 && 
118           <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
119         }
120         <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
121       </div>
122     );
123   }
124
125   nextPage(i: Inbox) { 
126     i.state.page++;
127     i.setState(i.state);
128     i.refetch();
129   }
130
131   prevPage(i: Inbox) { 
132     i.state.page--;
133     i.setState(i.state);
134     i.refetch();
135   }
136
137   handleUnreadTypeChange(i: Inbox, event: any) {
138     i.state.unreadType = Number(event.target.value);
139     i.state.page = 1;
140     i.setState(i.state);
141     i.refetch();
142   }
143
144   refetch() {
145     let form: GetRepliesForm = {
146       sort: SortType[this.state.sort],
147       unread_only: (this.state.unreadType == UnreadType.Unread),
148       page: this.state.page,
149       limit: 9999,
150     };
151     WebSocketService.Instance.getReplies(form);
152   }
153
154   handleSortChange(i: Inbox, event: any) {
155     i.state.sort = Number(event.target.value);
156     i.state.page = 1;
157     i.setState(i.state);
158     i.refetch();
159   }
160
161   markAllAsRead() {
162     WebSocketService.Instance.markAllAsRead();
163   }
164
165   parseMessage(msg: any) {
166     console.log(msg);
167     let op: UserOperation = msgOp(msg);
168     if (msg.error) {
169       alert(i18n.t(msg.error));
170       return;
171     } else if (op == UserOperation.GetReplies || op == UserOperation.MarkAllAsRead) {
172       let res: GetRepliesResponse = msg;
173       this.state.replies = res.replies;
174       this.sendRepliesCount();
175       window.scrollTo(0,0);
176       this.setState(this.state);
177     } else if (op == UserOperation.EditComment) {
178       let res: CommentResponse = msg;
179
180       let found = this.state.replies.find(c => c.id == res.comment.id);
181       found.content = res.comment.content;
182       found.updated = res.comment.updated;
183       found.removed = res.comment.removed;
184       found.deleted = res.comment.deleted;
185       found.upvotes = res.comment.upvotes;
186       found.downvotes = res.comment.downvotes;
187       found.score = res.comment.score;
188
189       // If youre in the unread view, just remove it from the list
190       if (this.state.unreadType == UnreadType.Unread && res.comment.read) {
191         this.state.replies = this.state.replies.filter(r => r.id !== res.comment.id);
192       } else {
193         let found = this.state.replies.find(c => c.id == res.comment.id);
194         found.read = res.comment.read;
195       }
196       this.sendRepliesCount();
197
198       this.setState(this.state);
199     } else if (op == UserOperation.CreateComment) {
200       // let res: CommentResponse = msg;
201       alert(i18n.t('reply_sent'));
202       // this.state.replies.unshift(res.comment); // TODO do this right
203       // this.setState(this.state);
204     } else if (op == UserOperation.SaveComment) {
205       let res: CommentResponse = msg;
206       let found = this.state.replies.find(c => c.id == res.comment.id);
207       found.saved = res.comment.saved;
208       this.setState(this.state);
209     } else if (op == UserOperation.CreateCommentLike) {
210       let res: CommentResponse = msg;
211       let found: Comment = this.state.replies.find(c => c.id === res.comment.id);
212       found.score = res.comment.score;
213       found.upvotes = res.comment.upvotes;
214       found.downvotes = res.comment.downvotes;
215       if (res.comment.my_vote !== null) 
216         found.my_vote = res.comment.my_vote;
217       this.setState(this.state);
218     }
219   }
220
221   sendRepliesCount() {
222     UserService.Instance.sub.next({user: UserService.Instance.user, unreadCount: this.state.replies.filter(r => !r.read).length});
223   }
224 }
225