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