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