]> Untitled Git - lemmy.git/blob - ui/src/components/sidebar.tsx
Merge branch 'master' into federation
[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
20 interface SidebarProps {
21   community: Community;
22   moderators: Array<CommunityUser>;
23   admins: Array<UserView>;
24   online: number;
25 }
26
27 interface SidebarState {
28   showEdit: boolean;
29   showRemoveDialog: boolean;
30   removeReason: string;
31   removeExpires: string;
32 }
33
34 export class Sidebar extends Component<SidebarProps, SidebarState> {
35   private emptyState: SidebarState = {
36     showEdit: false,
37     showRemoveDialog: false,
38     removeReason: null,
39     removeExpires: null,
40   };
41
42   constructor(props: any, context: any) {
43     super(props, context);
44     this.state = this.emptyState;
45     this.handleEditCommunity = this.handleEditCommunity.bind(this);
46     this.handleEditCancel = this.handleEditCancel.bind(this);
47   }
48
49   render() {
50     return (
51       <div>
52         {!this.state.showEdit ? (
53           this.sidebar()
54         ) : (
55           <CommunityForm
56             community={this.props.community}
57             onEdit={this.handleEditCommunity}
58             onCancel={this.handleEditCancel}
59           />
60         )}
61       </div>
62     );
63   }
64
65   sidebar() {
66     let community = this.props.community;
67     return (
68       <div>
69         <div class="card border-secondary mb-3">
70           <div class="card-body">
71             <h5 className="mb-0">
72               <span>{community.title}</span>
73               {community.removed && (
74                 <small className="ml-2 text-muted font-italic">
75                   {i18n.t('removed')}
76                 </small>
77               )}
78               {community.deleted && (
79                 <small className="ml-2 text-muted font-italic">
80                   {i18n.t('deleted')}
81                 </small>
82               )}
83             </h5>
84             <Link className="text-muted" to={`/c/${community.name}`}>
85               /c/{community.name}
86             </Link>
87             <ul class="list-inline mb-1 text-muted font-weight-bold">
88               {this.canMod && (
89                 <>
90                   <li className="list-inline-item-action">
91                     <span
92                       class="pointer"
93                       onClick={linkEvent(this, this.handleEditClick)}
94                       data-tippy-content={i18n.t('edit')}
95                     >
96                       <svg class="icon icon-inline">
97                         <use xlinkHref="#icon-edit"></use>
98                       </svg>
99                     </span>
100                   </li>
101                   {this.amCreator && (
102                     <li className="list-inline-item-action">
103                       <span
104                         class="pointer"
105                         onClick={linkEvent(this, this.handleDeleteClick)}
106                         data-tippy-content={
107                           !community.deleted
108                             ? i18n.t('delete')
109                             : i18n.t('restore')
110                         }
111                       >
112                         <svg
113                           class={`icon icon-inline ${community.deleted &&
114                             'text-danger'}`}
115                         >
116                           <use xlinkHref="#icon-trash"></use>
117                         </svg>
118                       </span>
119                     </li>
120                   )}
121                 </>
122               )}
123               {this.canAdmin && (
124                 <li className="list-inline-item">
125                   {!this.props.community.removed ? (
126                     <span
127                       class="pointer"
128                       onClick={linkEvent(this, this.handleModRemoveShow)}
129                     >
130                       {i18n.t('remove')}
131                     </span>
132                   ) : (
133                     <span
134                       class="pointer"
135                       onClick={linkEvent(this, this.handleModRemoveSubmit)}
136                     >
137                       {i18n.t('restore')}
138                     </span>
139                   )}
140                 </li>
141               )}
142             </ul>
143             {this.state.showRemoveDialog && (
144               <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
145                 <div class="form-group row">
146                   <label class="col-form-label" htmlFor="remove-reason">
147                     {i18n.t('reason')}
148                   </label>
149                   <input
150                     type="text"
151                     id="remove-reason"
152                     class="form-control mr-2"
153                     placeholder={i18n.t('optional')}
154                     value={this.state.removeReason}
155                     onInput={linkEvent(this, this.handleModRemoveReasonChange)}
156                   />
157                 </div>
158                 {/* TODO hold off on expires for now */}
159                 {/* <div class="form-group row"> */}
160                 {/*   <label class="col-form-label">Expires</label> */}
161                 {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
162                 {/* </div> */}
163                 <div class="form-group row">
164                   <button type="submit" class="btn btn-secondary">
165                     {i18n.t('remove_community')}
166                   </button>
167                 </div>
168               </form>
169             )}
170             <ul class="my-1 list-inline">
171               <li className="list-inline-item badge badge-secondary">
172                 {i18n.t('number_online', { count: this.props.online })}
173               </li>
174               <li className="list-inline-item badge badge-secondary">
175                 {i18n.t('number_of_subscribers', {
176                   count: community.number_of_subscribers,
177                 })}
178               </li>
179               <li className="list-inline-item badge badge-secondary">
180                 {i18n.t('number_of_posts', {
181                   count: community.number_of_posts,
182                 })}
183               </li>
184               <li className="list-inline-item badge badge-secondary">
185                 {i18n.t('number_of_comments', {
186                   count: community.number_of_comments,
187                 })}
188               </li>
189               <li className="list-inline-item">
190                 <Link className="badge badge-secondary" to="/communities">
191                   {community.category_name}
192                 </Link>
193               </li>
194               <li className="list-inline-item">
195                 <Link
196                   className="badge badge-secondary"
197                   to={`/modlog/community/${this.props.community.id}`}
198                 >
199                   {i18n.t('modlog')}
200                 </Link>
201               </li>
202             </ul>
203             <ul class="list-inline small">
204               <li class="list-inline-item">{i18n.t('mods')}: </li>
205               {this.props.moderators.map(mod => (
206                 <li class="list-inline-item">
207                   <Link
208                     class="text-body font-weight-bold"
209                     to={`/u/${mod.user_name}`}
210                   >
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               {i18n.t('create_a_post')}
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                   {i18n.t('unsubscribe')}
239                 </button>
240               ) : (
241                 <button
242                   class="btn btn-sm btn-secondary btn-block"
243                   onClick={linkEvent(community.id, this.handleSubscribe)}
244                 >
245                   {i18n.t('subscribe')}
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 }