]> Untitled Git - lemmy-ui.git/blob - src/shared/components/modlog.tsx
Modlog, admin-settings, search, and user done.
[lemmy-ui.git] / src / shared / components / modlog.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Helmet } from 'inferno-helmet';
3 import { Link } from 'inferno-router';
4 import { Subscription } from 'rxjs';
5 import {
6   UserOperation,
7   GetModlogForm,
8   GetModlogResponse,
9   ModRemovePost,
10   ModLockPost,
11   ModStickyPost,
12   ModRemoveComment,
13   ModRemoveCommunity,
14   ModBanFromCommunity,
15   ModBan,
16   ModAddCommunity,
17   ModAdd,
18   WebSocketJsonResponse,
19   Site,
20 } from 'lemmy-js-client';
21 import { WebSocketService } from '../services';
22 import {
23   wsJsonToRes,
24   addTypeInfo,
25   fetchLimit,
26   toast,
27   setIsoData,
28   wsSubscribe,
29   isBrowser,
30   lemmyHttp,
31 } from '../utils';
32 import { MomentTime } from './moment-time';
33 import moment from 'moment';
34 import { i18n } from '../i18next';
35
36 interface ModlogState {
37   combined: {
38     type_: string;
39     data:
40       | ModRemovePost
41       | ModLockPost
42       | ModStickyPost
43       | ModRemoveCommunity
44       | ModAdd
45       | ModBan;
46   }[];
47   communityId?: number;
48   communityName?: string;
49   page: number;
50   site: Site;
51   loading: boolean;
52 }
53
54 export class Modlog extends Component<any, ModlogState> {
55   private isoData = setIsoData(this.context);
56   private subscription: Subscription;
57   private emptyState: ModlogState = {
58     combined: [],
59     page: 1,
60     loading: true,
61     site: this.isoData.site.site,
62   };
63
64   constructor(props: any, context: any) {
65     super(props, context);
66
67     this.state = this.emptyState;
68     this.state.communityId = this.props.match.params.community_id
69       ? Number(this.props.match.params.community_id)
70       : undefined;
71
72     this.parseMessage = this.parseMessage.bind(this);
73     this.subscription = wsSubscribe(this.parseMessage);
74
75     // Only fetch the data if coming from another route
76     if (this.isoData.path == this.context.router.route.match.url) {
77       let data = this.isoData.routeData[0];
78       this.setCombined(data);
79       this.state.loading = false;
80     } else {
81       this.refetch();
82     }
83   }
84
85   componentWillUnmount() {
86     if (isBrowser()) {
87       this.subscription.unsubscribe();
88     }
89   }
90
91   setCombined(res: GetModlogResponse) {
92     let removed_posts = addTypeInfo(res.removed_posts, 'removed_posts');
93     let locked_posts = addTypeInfo(res.locked_posts, 'locked_posts');
94     let stickied_posts = addTypeInfo(res.stickied_posts, 'stickied_posts');
95     let removed_comments = addTypeInfo(
96       res.removed_comments,
97       'removed_comments'
98     );
99     let removed_communities = addTypeInfo(
100       res.removed_communities,
101       'removed_communities'
102     );
103     let banned_from_community = addTypeInfo(
104       res.banned_from_community,
105       'banned_from_community'
106     );
107     let added_to_community = addTypeInfo(
108       res.added_to_community,
109       'added_to_community'
110     );
111     let added = addTypeInfo(res.added, 'added');
112     let banned = addTypeInfo(res.banned, 'banned');
113     this.state.combined = [];
114
115     this.state.combined.push(...removed_posts);
116     this.state.combined.push(...locked_posts);
117     this.state.combined.push(...stickied_posts);
118     this.state.combined.push(...removed_comments);
119     this.state.combined.push(...removed_communities);
120     this.state.combined.push(...banned_from_community);
121     this.state.combined.push(...added_to_community);
122     this.state.combined.push(...added);
123     this.state.combined.push(...banned);
124
125     if (this.state.communityId && this.state.combined.length > 0) {
126       this.state.communityName = (this.state.combined[0]
127         .data as ModRemovePost).community_name;
128     }
129
130     // Sort them by time
131     this.state.combined.sort((a, b) =>
132       b.data.when_.localeCompare(a.data.when_)
133     );
134   }
135
136   combined() {
137     return (
138       <tbody>
139         {this.state.combined.map(i => (
140           <tr>
141             <td>
142               <MomentTime data={i.data} />
143             </td>
144             <td>
145               <Link to={`/u/${i.data.mod_user_name}`}>
146                 {i.data.mod_user_name}
147               </Link>
148             </td>
149             <td>
150               {i.type_ == 'removed_posts' && (
151                 <>
152                   {(i.data as ModRemovePost).removed ? 'Removed' : 'Restored'}
153                   <span>
154                     {' '}
155                     Post{' '}
156                     <Link to={`/post/${(i.data as ModRemovePost).post_id}`}>
157                       {(i.data as ModRemovePost).post_name}
158                     </Link>
159                   </span>
160                   <div>
161                     {(i.data as ModRemovePost).reason &&
162                       ` reason: ${(i.data as ModRemovePost).reason}`}
163                   </div>
164                 </>
165               )}
166               {i.type_ == 'locked_posts' && (
167                 <>
168                   {(i.data as ModLockPost).locked ? 'Locked' : 'Unlocked'}
169                   <span>
170                     {' '}
171                     Post{' '}
172                     <Link to={`/post/${(i.data as ModLockPost).post_id}`}>
173                       {(i.data as ModLockPost).post_name}
174                     </Link>
175                   </span>
176                 </>
177               )}
178               {i.type_ == 'stickied_posts' && (
179                 <>
180                   {(i.data as ModStickyPost).stickied
181                     ? 'Stickied'
182                     : 'Unstickied'}
183                   <span>
184                     {' '}
185                     Post{' '}
186                     <Link to={`/post/${(i.data as ModStickyPost).post_id}`}>
187                       {(i.data as ModStickyPost).post_name}
188                     </Link>
189                   </span>
190                 </>
191               )}
192               {i.type_ == 'removed_comments' && (
193                 <>
194                   {(i.data as ModRemoveComment).removed
195                     ? 'Removed'
196                     : 'Restored'}
197                   <span>
198                     {' '}
199                     Comment{' '}
200                     <Link
201                       to={`/post/${
202                         (i.data as ModRemoveComment).post_id
203                       }/comment/${(i.data as ModRemoveComment).comment_id}`}
204                     >
205                       {(i.data as ModRemoveComment).comment_content}
206                     </Link>
207                   </span>
208                   <span>
209                     {' '}
210                     by{' '}
211                     <Link
212                       to={`/u/${
213                         (i.data as ModRemoveComment).comment_user_name
214                       }`}
215                     >
216                       {(i.data as ModRemoveComment).comment_user_name}
217                     </Link>
218                   </span>
219                   <div>
220                     {(i.data as ModRemoveComment).reason &&
221                       ` reason: ${(i.data as ModRemoveComment).reason}`}
222                   </div>
223                 </>
224               )}
225               {i.type_ == 'removed_communities' && (
226                 <>
227                   {(i.data as ModRemoveCommunity).removed
228                     ? 'Removed'
229                     : 'Restored'}
230                   <span>
231                     {' '}
232                     Community{' '}
233                     <Link
234                       to={`/c/${(i.data as ModRemoveCommunity).community_name}`}
235                     >
236                       {(i.data as ModRemoveCommunity).community_name}
237                     </Link>
238                   </span>
239                   <div>
240                     {(i.data as ModRemoveCommunity).reason &&
241                       ` reason: ${(i.data as ModRemoveCommunity).reason}`}
242                   </div>
243                   <div>
244                     {(i.data as ModRemoveCommunity).expires &&
245                       ` expires: ${moment
246                         .utc((i.data as ModRemoveCommunity).expires)
247                         .fromNow()}`}
248                   </div>
249                 </>
250               )}
251               {i.type_ == 'banned_from_community' && (
252                 <>
253                   <span>
254                     {(i.data as ModBanFromCommunity).banned
255                       ? 'Banned '
256                       : 'Unbanned '}{' '}
257                   </span>
258                   <span>
259                     <Link
260                       to={`/u/${
261                         (i.data as ModBanFromCommunity).other_user_name
262                       }`}
263                     >
264                       {(i.data as ModBanFromCommunity).other_user_name}
265                     </Link>
266                   </span>
267                   <span> from the community </span>
268                   <span>
269                     <Link
270                       to={`/c/${
271                         (i.data as ModBanFromCommunity).community_name
272                       }`}
273                     >
274                       {(i.data as ModBanFromCommunity).community_name}
275                     </Link>
276                   </span>
277                   <div>
278                     {(i.data as ModBanFromCommunity).reason &&
279                       ` reason: ${(i.data as ModBanFromCommunity).reason}`}
280                   </div>
281                   <div>
282                     {(i.data as ModBanFromCommunity).expires &&
283                       ` expires: ${moment
284                         .utc((i.data as ModBanFromCommunity).expires)
285                         .fromNow()}`}
286                   </div>
287                 </>
288               )}
289               {i.type_ == 'added_to_community' && (
290                 <>
291                   <span>
292                     {(i.data as ModAddCommunity).removed
293                       ? 'Removed '
294                       : 'Appointed '}{' '}
295                   </span>
296                   <span>
297                     <Link
298                       to={`/u/${(i.data as ModAddCommunity).other_user_name}`}
299                     >
300                       {(i.data as ModAddCommunity).other_user_name}
301                     </Link>
302                   </span>
303                   <span> as a mod to the community </span>
304                   <span>
305                     <Link
306                       to={`/c/${(i.data as ModAddCommunity).community_name}`}
307                     >
308                       {(i.data as ModAddCommunity).community_name}
309                     </Link>
310                   </span>
311                 </>
312               )}
313               {i.type_ == 'banned' && (
314                 <>
315                   <span>
316                     {(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '}{' '}
317                   </span>
318                   <span>
319                     <Link to={`/u/${(i.data as ModBan).other_user_name}`}>
320                       {(i.data as ModBan).other_user_name}
321                     </Link>
322                   </span>
323                   <div>
324                     {(i.data as ModBan).reason &&
325                       ` reason: ${(i.data as ModBan).reason}`}
326                   </div>
327                   <div>
328                     {(i.data as ModBan).expires &&
329                       ` expires: ${moment
330                         .utc((i.data as ModBan).expires)
331                         .fromNow()}`}
332                   </div>
333                 </>
334               )}
335               {i.type_ == 'added' && (
336                 <>
337                   <span>
338                     {(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '}{' '}
339                   </span>
340                   <span>
341                     <Link to={`/u/${(i.data as ModAdd).other_user_name}`}>
342                       {(i.data as ModAdd).other_user_name}
343                     </Link>
344                   </span>
345                   <span> as an admin </span>
346                 </>
347               )}
348             </td>
349           </tr>
350         ))}
351       </tbody>
352     );
353   }
354
355   get documentTitle(): string {
356     if (this.state.site) {
357       return `Modlog - ${this.state.site.name}`;
358     } else {
359       return 'Lemmy';
360     }
361   }
362
363   render() {
364     return (
365       <div class="container">
366         <Helmet title={this.documentTitle} />
367         {this.state.loading ? (
368           <h5 class="">
369             <svg class="icon icon-spinner spin">
370               <use xlinkHref="#icon-spinner"></use>
371             </svg>
372           </h5>
373         ) : (
374           <div>
375             <h5>
376               {this.state.communityName && (
377                 <Link
378                   className="text-body"
379                   to={`/c/${this.state.communityName}`}
380                 >
381                   /c/{this.state.communityName}{' '}
382                 </Link>
383               )}
384               <span>{i18n.t('modlog')}</span>
385             </h5>
386             <div class="table-responsive">
387               <table id="modlog_table" class="table table-sm table-hover">
388                 <thead class="pointer">
389                   <tr>
390                     <th> {i18n.t('time')}</th>
391                     <th>{i18n.t('mod')}</th>
392                     <th>{i18n.t('action')}</th>
393                   </tr>
394                 </thead>
395                 {this.combined()}
396               </table>
397               {this.paginator()}
398             </div>
399           </div>
400         )}
401       </div>
402     );
403   }
404
405   paginator() {
406     return (
407       <div class="mt-2">
408         {this.state.page > 1 && (
409           <button
410             class="btn btn-secondary mr-1"
411             onClick={linkEvent(this, this.prevPage)}
412           >
413             {i18n.t('prev')}
414           </button>
415         )}
416         <button
417           class="btn btn-secondary"
418           onClick={linkEvent(this, this.nextPage)}
419         >
420           {i18n.t('next')}
421         </button>
422       </div>
423     );
424   }
425
426   nextPage(i: Modlog) {
427     i.state.page++;
428     i.setState(i.state);
429     i.refetch();
430   }
431
432   prevPage(i: Modlog) {
433     i.state.page--;
434     i.setState(i.state);
435     i.refetch();
436   }
437
438   refetch() {
439     let modlogForm: GetModlogForm = {
440       community_id: this.state.communityId,
441       page: this.state.page,
442       limit: fetchLimit,
443     };
444     WebSocketService.Instance.getModlog(modlogForm);
445   }
446
447   static fetchInitialData(_auth: string, path: string): Promise<any>[] {
448     let pathSplit = path.split('/');
449     let communityId = pathSplit[3];
450     let promises: Promise<any>[] = [];
451
452     let modlogForm: GetModlogForm = {
453       page: 1,
454       limit: fetchLimit,
455     };
456
457     if (communityId) {
458       modlogForm.community_id = Number(communityId);
459     }
460
461     promises.push(lemmyHttp.getModlog(modlogForm));
462     return promises;
463   }
464
465   parseMessage(msg: WebSocketJsonResponse) {
466     console.log(msg);
467     let res = wsJsonToRes(msg);
468     if (msg.error) {
469       toast(i18n.t(msg.error), 'danger');
470       return;
471     } else if (res.op == UserOperation.GetModlog) {
472       let data = res.data as GetModlogResponse;
473       this.state.loading = false;
474       window.scrollTo(0, 0);
475       this.setCombined(data);
476       this.setState(this.state);
477     }
478   }
479 }