]> Untitled Git - lemmy.git/blob - ui/src/components/sidebar.tsx
Merge pull request #687 from LemmyNet/undo_delete_community
[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               <li className="list-inline-item badge badge-secondary">
177                 {i18n.t('number_online', { count: this.props.online })}
178               </li>
179               <li className="list-inline-item badge badge-secondary">
180                 {i18n.t('number_of_subscribers', {
181                   count: community.number_of_subscribers,
182                 })}
183               </li>
184               <li className="list-inline-item badge badge-secondary">
185                 {i18n.t('number_of_posts', {
186                   count: community.number_of_posts,
187                 })}
188               </li>
189               <li className="list-inline-item badge badge-secondary">
190                 {i18n.t('number_of_comments', {
191                   count: community.number_of_comments,
192                 })}
193               </li>
194               <li className="list-inline-item">
195                 <Link className="badge badge-secondary" to="/communities">
196                   {community.category_name}
197                 </Link>
198               </li>
199               <li className="list-inline-item">
200                 <Link
201                   className="badge badge-secondary"
202                   to={`/modlog/community/${this.props.community.id}`}
203                 >
204                   {i18n.t('modlog')}
205                 </Link>
206               </li>
207             </ul>
208             <ul class="list-inline small">
209               <li class="list-inline-item">{i18n.t('mods')}: </li>
210               {this.props.moderators.map(mod => (
211                 <li class="list-inline-item">
212                   <UserListing
213                     user={{
214                       name: mod.user_name,
215                       avatar: mod.avatar,
216                       id: mod.user_id,
217                       local: mod.user_local,
218                       actor_id: mod.user_actor_id,
219                     }}
220                   />
221                 </li>
222               ))}
223             </ul>
224             {/* TODO the to= needs to be able to handle community_ids as well, since they're federated */}
225             <Link
226               class={`btn btn-sm btn-secondary btn-block mb-3 ${
227                 (community.deleted || community.removed) && 'no-click'
228               }`}
229               to={`/create_post?community=${community.name}`}
230             >
231               {i18n.t('create_a_post')}
232             </Link>
233             <div>
234               {community.subscribed ? (
235                 <button
236                   class="btn btn-sm btn-secondary btn-block"
237                   onClick={linkEvent(community.id, this.handleUnsubscribe)}
238                 >
239                   {i18n.t('unsubscribe')}
240                 </button>
241               ) : (
242                 <button
243                   class="btn btn-sm btn-secondary btn-block"
244                   onClick={linkEvent(community.id, this.handleSubscribe)}
245                 >
246                   {i18n.t('subscribe')}
247                 </button>
248               )}
249             </div>
250           </div>
251         </div>
252         {community.description && (
253           <div class="card border-secondary">
254             <div class="card-body">
255               <div
256                 className="md-div"
257                 dangerouslySetInnerHTML={mdToHtml(community.description)}
258               />
259             </div>
260           </div>
261         )}
262       </div>
263     );
264   }
265
266   handleEditClick(i: Sidebar) {
267     i.state.showEdit = true;
268     i.setState(i.state);
269   }
270
271   handleEditCommunity() {
272     this.state.showEdit = false;
273     this.setState(this.state);
274   }
275
276   handleEditCancel() {
277     this.state.showEdit = false;
278     this.setState(this.state);
279   }
280
281   handleDeleteClick(i: Sidebar) {
282     event.preventDefault();
283     let deleteForm: CommunityFormI = {
284       name: i.props.community.name,
285       title: i.props.community.title,
286       category_id: i.props.community.category_id,
287       edit_id: i.props.community.id,
288       deleted: !i.props.community.deleted,
289       nsfw: i.props.community.nsfw,
290       auth: null,
291     };
292     WebSocketService.Instance.editCommunity(deleteForm);
293   }
294
295   handleUnsubscribe(communityId: number) {
296     let form: FollowCommunityForm = {
297       community_id: communityId,
298       follow: false,
299     };
300     WebSocketService.Instance.followCommunity(form);
301   }
302
303   handleSubscribe(communityId: number) {
304     let form: FollowCommunityForm = {
305       community_id: communityId,
306       follow: true,
307     };
308     WebSocketService.Instance.followCommunity(form);
309   }
310
311   private get amCreator(): boolean {
312     return this.props.community.creator_id == UserService.Instance.user.id;
313   }
314
315   get canMod(): boolean {
316     return (
317       UserService.Instance.user &&
318       this.props.moderators
319         .map(m => m.user_id)
320         .includes(UserService.Instance.user.id)
321     );
322   }
323
324   get canAdmin(): boolean {
325     return (
326       UserService.Instance.user &&
327       this.props.admins.map(a => a.id).includes(UserService.Instance.user.id)
328     );
329   }
330
331   handleModRemoveShow(i: Sidebar) {
332     i.state.showRemoveDialog = true;
333     i.setState(i.state);
334   }
335
336   handleModRemoveReasonChange(i: Sidebar, event: any) {
337     i.state.removeReason = event.target.value;
338     i.setState(i.state);
339   }
340
341   handleModRemoveExpiresChange(i: Sidebar, event: any) {
342     console.log(event.target.value);
343     i.state.removeExpires = event.target.value;
344     i.setState(i.state);
345   }
346
347   handleModRemoveSubmit(i: Sidebar) {
348     event.preventDefault();
349     let deleteForm: CommunityFormI = {
350       name: i.props.community.name,
351       title: i.props.community.title,
352       category_id: i.props.community.category_id,
353       edit_id: i.props.community.id,
354       removed: !i.props.community.removed,
355       reason: i.state.removeReason,
356       expires: getUnixTime(i.state.removeExpires),
357       nsfw: i.props.community.nsfw,
358       auth: null,
359     };
360     WebSocketService.Instance.editCommunity(deleteForm);
361
362     i.state.showRemoveDialog = false;
363     i.setState(i.state);
364   }
365 }