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