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