]> Untitled Git - lemmy.git/blob - ui/src/components/sidebar.tsx
Merge remote-tracking branch 'weblate/master'
[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   pictrsAvatarThumbnail,
15   showAvatars,
16 } from '../utils';
17 import { CommunityForm } from './community-form';
18 import { UserListing } from './user-listing';
19 import { i18n } from '../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                   {i18n.t('removed')}
77                 </small>
78               )}
79               {community.deleted && (
80                 <small className="ml-2 text-muted font-italic">
81                   {i18n.t('deleted')}
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 font-weight-bold">
89               {this.canMod && (
90                 <>
91                   <li className="list-inline-item-action">
92                     <span
93                       class="pointer"
94                       onClick={linkEvent(this, this.handleEditClick)}
95                       data-tippy-content={i18n.t('edit')}
96                     >
97                       <svg class="icon icon-inline">
98                         <use xlinkHref="#icon-edit"></use>
99                       </svg>
100                     </span>
101                   </li>
102                   {this.amCreator && (
103                     <li className="list-inline-item-action">
104                       <span
105                         class="pointer"
106                         onClick={linkEvent(this, this.handleDeleteClick)}
107                         data-tippy-content={
108                           !community.deleted
109                             ? i18n.t('delete')
110                             : i18n.t('restore')
111                         }
112                       >
113                         <svg
114                           class={`icon icon-inline ${
115                             community.deleted && 'text-danger'
116                           }`}
117                         >
118                           <use xlinkHref="#icon-trash"></use>
119                         </svg>
120                       </span>
121                     </li>
122                   )}
123                 </>
124               )}
125               {this.canAdmin && (
126                 <li className="list-inline-item">
127                   {!this.props.community.removed ? (
128                     <span
129                       class="pointer"
130                       onClick={linkEvent(this, this.handleModRemoveShow)}
131                     >
132                       {i18n.t('remove')}
133                     </span>
134                   ) : (
135                     <span
136                       class="pointer"
137                       onClick={linkEvent(this, this.handleModRemoveSubmit)}
138                     >
139                       {i18n.t('restore')}
140                     </span>
141                   )}
142                 </li>
143               )}
144             </ul>
145             {this.state.showRemoveDialog && (
146               <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
147                 <div class="form-group row">
148                   <label class="col-form-label" htmlFor="remove-reason">
149                     {i18n.t('reason')}
150                   </label>
151                   <input
152                     type="text"
153                     id="remove-reason"
154                     class="form-control mr-2"
155                     placeholder={i18n.t('optional')}
156                     value={this.state.removeReason}
157                     onInput={linkEvent(this, this.handleModRemoveReasonChange)}
158                   />
159                 </div>
160                 {/* TODO hold off on expires for now */}
161                 {/* <div class="form-group row"> */}
162                 {/*   <label class="col-form-label">Expires</label> */}
163                 {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
164                 {/* </div> */}
165                 <div class="form-group row">
166                   <button type="submit" class="btn btn-secondary">
167                     {i18n.t('remove_community')}
168                   </button>
169                 </div>
170               </form>
171             )}
172             <ul class="my-1 list-inline">
173               {/*
174               <li className="list-inline-item badge badge-secondary">
175                 {i18n.t('number_online', { count: this.props.online })}
176               </li>
177               */}
178               <li className="list-inline-item badge badge-secondary">
179                 {i18n.t('number_of_subscribers', {
180                   count: community.number_of_subscribers,
181                 })}
182               </li>
183               <li className="list-inline-item badge badge-secondary">
184                 {i18n.t('number_of_posts', {
185                   count: community.number_of_posts,
186                 })}
187               </li>
188               <li className="list-inline-item badge badge-secondary">
189                 {i18n.t('number_of_comments', {
190                   count: community.number_of_comments,
191                 })}
192               </li>
193               <li className="list-inline-item">
194                 <Link className="badge badge-secondary" to="/communities">
195                   {community.category_name}
196                 </Link>
197               </li>
198               <li className="list-inline-item">
199                 <Link
200                   className="badge badge-secondary"
201                   to={`/modlog/community/${this.props.community.id}`}
202                 >
203                   {i18n.t('modlog')}
204                 </Link>
205               </li>
206             </ul>
207             <ul class="list-inline small">
208               <li class="list-inline-item">{i18n.t('mods')}: </li>
209               {this.props.moderators.map(mod => (
210                 <li class="list-inline-item">
211                   <UserListing
212                     user={{
213                       name: mod.user_name,
214                       avatar: mod.avatar,
215                     }}
216                   />
217                 </li>
218               ))}
219             </ul>
220             <Link
221               class={`btn btn-sm btn-secondary btn-block mb-3 ${
222                 (community.deleted || community.removed) && 'no-click'
223               }`}
224               to={`/create_post?community=${community.name}`}
225             >
226               {i18n.t('create_a_post')}
227             </Link>
228             <div>
229               {community.subscribed ? (
230                 <button
231                   class="btn btn-sm btn-secondary btn-block"
232                   onClick={linkEvent(community.id, this.handleUnsubscribe)}
233                 >
234                   {i18n.t('unsubscribe')}
235                 </button>
236               ) : (
237                 <button
238                   class="btn btn-sm btn-secondary btn-block"
239                   onClick={linkEvent(community.id, this.handleSubscribe)}
240                 >
241                   {i18n.t('subscribe')}
242                 </button>
243               )}
244             </div>
245           </div>
246         </div>
247         {community.description && (
248           <div class="card border-secondary">
249             <div class="card-body">
250               <div
251                 className="md-div"
252                 dangerouslySetInnerHTML={mdToHtml(community.description)}
253               />
254             </div>
255           </div>
256         )}
257       </div>
258     );
259   }
260
261   handleEditClick(i: Sidebar) {
262     i.state.showEdit = true;
263     i.setState(i.state);
264   }
265
266   handleEditCommunity() {
267     this.state.showEdit = false;
268     this.setState(this.state);
269   }
270
271   handleEditCancel() {
272     this.state.showEdit = false;
273     this.setState(this.state);
274   }
275
276   handleDeleteClick(i: Sidebar) {
277     event.preventDefault();
278     let deleteForm: CommunityFormI = {
279       name: i.props.community.name,
280       title: i.props.community.title,
281       category_id: i.props.community.category_id,
282       edit_id: i.props.community.id,
283       deleted: !i.props.community.deleted,
284       nsfw: i.props.community.nsfw,
285       auth: null,
286     };
287     WebSocketService.Instance.editCommunity(deleteForm);
288   }
289
290   handleUnsubscribe(communityId: number) {
291     let form: FollowCommunityForm = {
292       community_id: communityId,
293       follow: false,
294     };
295     WebSocketService.Instance.followCommunity(form);
296   }
297
298   handleSubscribe(communityId: number) {
299     let form: FollowCommunityForm = {
300       community_id: communityId,
301       follow: true,
302     };
303     WebSocketService.Instance.followCommunity(form);
304   }
305
306   private get amCreator(): boolean {
307     return this.props.community.creator_id == UserService.Instance.user.id;
308   }
309
310   get canMod(): boolean {
311     return (
312       UserService.Instance.user &&
313       this.props.moderators
314         .map(m => m.user_id)
315         .includes(UserService.Instance.user.id)
316     );
317   }
318
319   get canAdmin(): boolean {
320     return (
321       UserService.Instance.user &&
322       this.props.admins.map(a => a.id).includes(UserService.Instance.user.id)
323     );
324   }
325
326   handleModRemoveShow(i: Sidebar) {
327     i.state.showRemoveDialog = true;
328     i.setState(i.state);
329   }
330
331   handleModRemoveReasonChange(i: Sidebar, event: any) {
332     i.state.removeReason = event.target.value;
333     i.setState(i.state);
334   }
335
336   handleModRemoveExpiresChange(i: Sidebar, event: any) {
337     console.log(event.target.value);
338     i.state.removeExpires = event.target.value;
339     i.setState(i.state);
340   }
341
342   handleModRemoveSubmit(i: Sidebar) {
343     event.preventDefault();
344     let deleteForm: CommunityFormI = {
345       name: i.props.community.name,
346       title: i.props.community.title,
347       category_id: i.props.community.category_id,
348       edit_id: i.props.community.id,
349       removed: !i.props.community.removed,
350       reason: i.state.removeReason,
351       expires: getUnixTime(i.state.removeExpires),
352       nsfw: i.props.community.nsfw,
353       auth: null,
354     };
355     WebSocketService.Instance.editCommunity(deleteForm);
356
357     i.state.showRemoveDialog = false;
358     i.setState(i.state);
359   }
360 }