]> Untitled Git - lemmy.git/blob - ui/src/components/sidebar.tsx
Merge branch 'dev' into websocket_scopes
[lemmy.git] / ui / src / components / sidebar.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
3 import {
4   Community,
5   CommunityUser,
6   FollowCommunityForm,
7   CommunityForm as CommunityFormI,
8   UserView,
9 } from '../interfaces';
10 import { WebSocketService, UserService } from '../services';
11 import {
12   mdToHtml,
13   getUnixTime,
14   pictshareAvatarThumbnail,
15   showAvatars,
16 } from '../utils';
17 import { CommunityForm } from './community-form';
18 import { i18n } from '../i18next';
19 import { T } from 'inferno-i18next';
20
21 interface SidebarProps {
22   community: Community;
23   moderators: Array<CommunityUser>;
24   admins: Array<UserView>;
25   online: number;
26 }
27
28 interface SidebarState {
29   showEdit: boolean;
30   showRemoveDialog: boolean;
31   removeReason: string;
32   removeExpires: string;
33 }
34
35 export class Sidebar extends Component<SidebarProps, SidebarState> {
36   private emptyState: SidebarState = {
37     showEdit: false,
38     showRemoveDialog: false,
39     removeReason: null,
40     removeExpires: null,
41   };
42
43   constructor(props: any, context: any) {
44     super(props, context);
45     this.state = this.emptyState;
46     this.handleEditCommunity = this.handleEditCommunity.bind(this);
47     this.handleEditCancel = this.handleEditCancel.bind(this);
48   }
49
50   render() {
51     return (
52       <div>
53         {!this.state.showEdit ? (
54           this.sidebar()
55         ) : (
56           <CommunityForm
57             community={this.props.community}
58             onEdit={this.handleEditCommunity}
59             onCancel={this.handleEditCancel}
60           />
61         )}
62       </div>
63     );
64   }
65
66   sidebar() {
67     let community = this.props.community;
68     return (
69       <div>
70         <div class="card border-secondary mb-3">
71           <div class="card-body">
72             <h5 className="mb-0">
73               <span>{community.title}</span>
74               {community.removed && (
75                 <small className="ml-2 text-muted font-italic">
76                   <T i18nKey="removed">#</T>
77                 </small>
78               )}
79               {community.deleted && (
80                 <small className="ml-2 text-muted font-italic">
81                   <T i18nKey="deleted">#</T>
82                 </small>
83               )}
84             </h5>
85             <Link className="text-muted" to={`/c/${community.name}`}>
86               /c/{community.name}
87             </Link>
88             <ul class="list-inline mb-1 text-muted small font-weight-bold">
89               {this.canMod && (
90                 <>
91                   <li className="list-inline-item">
92                     <span
93                       class="pointer"
94                       onClick={linkEvent(this, this.handleEditClick)}
95                     >
96                       <T i18nKey="edit">#</T>
97                     </span>
98                   </li>
99                   {this.amCreator && (
100                     <li className="list-inline-item">
101                       <span
102                         class="pointer"
103                         onClick={linkEvent(this, this.handleDeleteClick)}
104                       >
105                         {!community.deleted
106                           ? i18n.t('delete')
107                           : i18n.t('restore')}
108                       </span>
109                     </li>
110                   )}
111                 </>
112               )}
113               {this.canAdmin && (
114                 <li className="list-inline-item">
115                   {!this.props.community.removed ? (
116                     <span
117                       class="pointer"
118                       onClick={linkEvent(this, this.handleModRemoveShow)}
119                     >
120                       <T i18nKey="remove">#</T>
121                     </span>
122                   ) : (
123                     <span
124                       class="pointer"
125                       onClick={linkEvent(this, this.handleModRemoveSubmit)}
126                     >
127                       <T i18nKey="restore">#</T>
128                     </span>
129                   )}
130                 </li>
131               )}
132             </ul>
133             {this.state.showRemoveDialog && (
134               <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
135                 <div class="form-group row">
136                   <label class="col-form-label">
137                     <T i18nKey="reason">#</T>
138                   </label>
139                   <input
140                     type="text"
141                     class="form-control mr-2"
142                     placeholder={i18n.t('optional')}
143                     value={this.state.removeReason}
144                     onInput={linkEvent(this, this.handleModRemoveReasonChange)}
145                   />
146                 </div>
147                 {/* TODO hold off on expires for now */}
148                 {/* <div class="form-group row"> */}
149                 {/*   <label class="col-form-label">Expires</label> */}
150                 {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
151                 {/* </div> */}
152                 <div class="form-group row">
153                   <button type="submit" class="btn btn-secondary">
154                     <T i18nKey="remove_community">#</T>
155                   </button>
156                 </div>
157               </form>
158             )}
159             <ul class="my-1 list-inline">
160               <li className="list-inline-item badge badge-secondary">
161                 <T
162                   i18nKey="number_online"
163                   interpolation={{ count: this.props.online }}
164                 >
165                   #
166                 </T>
167               </li>
168               <li className="list-inline-item badge badge-secondary">
169                 <T
170                   i18nKey="number_of_subscribers"
171                   interpolation={{ count: community.number_of_subscribers }}
172                 >
173                   #
174                 </T>
175               </li>
176               <li className="list-inline-item badge badge-secondary">
177                 <T
178                   i18nKey="number_of_posts"
179                   interpolation={{ count: community.number_of_posts }}
180                 >
181                   #
182                 </T>
183               </li>
184               <li className="list-inline-item badge badge-secondary">
185                 <T
186                   i18nKey="number_of_comments"
187                   interpolation={{ count: community.number_of_comments }}
188                 >
189                   #
190                 </T>
191               </li>
192               <li className="list-inline-item">
193                 <Link className="badge badge-secondary" to="/communities">
194                   {community.category_name}
195                 </Link>
196               </li>
197               <li className="list-inline-item">
198                 <Link
199                   className="badge badge-secondary"
200                   to={`/modlog/community/${this.props.community.id}`}
201                 >
202                   <T i18nKey="modlog">#</T>
203                 </Link>
204               </li>
205             </ul>
206             <ul class="list-inline small">
207               <li class="list-inline-item">{i18n.t('mods')}: </li>
208               {this.props.moderators.map(mod => (
209                 <li class="list-inline-item">
210                   <Link class="text-info" to={`/u/${mod.user_name}`}>
211                     {mod.avatar && showAvatars() && (
212                       <img
213                         height="32"
214                         width="32"
215                         src={pictshareAvatarThumbnail(mod.avatar)}
216                         class="rounded-circle mr-1"
217                       />
218                     )}
219                     <span>{mod.user_name}</span>
220                   </Link>
221                 </li>
222               ))}
223             </ul>
224             <Link
225               class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted ||
226                 community.removed) &&
227                 'no-click'}`}
228               to={`/create_post?community=${community.name}`}
229             >
230               <T i18nKey="create_a_post">#</T>
231             </Link>
232             <div>
233               {community.subscribed ? (
234                 <button
235                   class="btn btn-sm btn-secondary btn-block"
236                   onClick={linkEvent(community.id, this.handleUnsubscribe)}
237                 >
238                   <T i18nKey="unsubscribe">#</T>
239                 </button>
240               ) : (
241                 <button
242                   class="btn btn-sm btn-secondary btn-block"
243                   onClick={linkEvent(community.id, this.handleSubscribe)}
244                 >
245                   <T i18nKey="subscribe">#</T>
246                 </button>
247               )}
248             </div>
249           </div>
250         </div>
251         {community.description && (
252           <div class="card border-secondary">
253             <div class="card-body">
254               <div
255                 className="md-div"
256                 dangerouslySetInnerHTML={mdToHtml(community.description)}
257               />
258             </div>
259           </div>
260         )}
261       </div>
262     );
263   }
264
265   handleEditClick(i: Sidebar) {
266     i.state.showEdit = true;
267     i.setState(i.state);
268   }
269
270   handleEditCommunity() {
271     this.state.showEdit = false;
272     this.setState(this.state);
273   }
274
275   handleEditCancel() {
276     this.state.showEdit = false;
277     this.setState(this.state);
278   }
279
280   handleDeleteClick(i: Sidebar) {
281     event.preventDefault();
282     let deleteForm: CommunityFormI = {
283       name: i.props.community.name,
284       title: i.props.community.title,
285       category_id: i.props.community.category_id,
286       edit_id: i.props.community.id,
287       deleted: !i.props.community.deleted,
288       nsfw: i.props.community.nsfw,
289       auth: null,
290     };
291     WebSocketService.Instance.editCommunity(deleteForm);
292   }
293
294   handleUnsubscribe(communityId: number) {
295     let form: FollowCommunityForm = {
296       community_id: communityId,
297       follow: false,
298     };
299     WebSocketService.Instance.followCommunity(form);
300   }
301
302   handleSubscribe(communityId: number) {
303     let form: FollowCommunityForm = {
304       community_id: communityId,
305       follow: true,
306     };
307     WebSocketService.Instance.followCommunity(form);
308   }
309
310   private get amCreator(): boolean {
311     return this.props.community.creator_id == UserService.Instance.user.id;
312   }
313
314   get canMod(): boolean {
315     return (
316       UserService.Instance.user &&
317       this.props.moderators
318         .map(m => m.user_id)
319         .includes(UserService.Instance.user.id)
320     );
321   }
322
323   get canAdmin(): boolean {
324     return (
325       UserService.Instance.user &&
326       this.props.admins.map(a => a.id).includes(UserService.Instance.user.id)
327     );
328   }
329
330   handleModRemoveShow(i: Sidebar) {
331     i.state.showRemoveDialog = true;
332     i.setState(i.state);
333   }
334
335   handleModRemoveReasonChange(i: Sidebar, event: any) {
336     i.state.removeReason = event.target.value;
337     i.setState(i.state);
338   }
339
340   handleModRemoveExpiresChange(i: Sidebar, event: any) {
341     console.log(event.target.value);
342     i.state.removeExpires = event.target.value;
343     i.setState(i.state);
344   }
345
346   handleModRemoveSubmit(i: Sidebar) {
347     event.preventDefault();
348     let deleteForm: CommunityFormI = {
349       name: i.props.community.name,
350       title: i.props.community.title,
351       category_id: i.props.community.category_id,
352       edit_id: i.props.community.id,
353       removed: !i.props.community.removed,
354       reason: i.state.removeReason,
355       expires: getUnixTime(i.state.removeExpires),
356       nsfw: i.props.community.nsfw,
357       auth: null,
358     };
359     WebSocketService.Instance.editCommunity(deleteForm);
360
361     i.state.showRemoveDialog = false;
362     i.setState(i.state);
363   }
364 }