]> Untitled Git - lemmy.git/blob - ui/src/components/sidebar.tsx
Second front end pass.
[lemmy.git] / ui / src / components / sidebar.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
3 import { Community, CommunityUser, FollowCommunityForm, CommunityForm as CommunityFormI, UserView } from '../interfaces';
4 import { WebSocketService, UserService } from '../services';
5 import { mdToHtml, getUnixTime } from '../utils';
6 import { CommunityForm } from './community-form';
7 import { i18n } from '../i18next';
8 import { T } from 'inferno-i18next';
9
10 interface SidebarProps {
11   community: Community;
12   moderators: Array<CommunityUser>;
13   admins: Array<UserView>;
14 }
15
16 interface SidebarState {
17   showEdit: boolean;
18   showRemoveDialog: boolean;
19   removeReason: string;
20   removeExpires: string;
21 }
22
23 export class Sidebar extends Component<SidebarProps, SidebarState> {
24
25   private emptyState: SidebarState = {
26     showEdit: false,
27     showRemoveDialog: false,
28     removeReason: null,
29     removeExpires: null
30   }
31
32   constructor(props: any, context: any) {
33     super(props, context);
34     this.state = this.emptyState;
35     this.handleEditCommunity = this.handleEditCommunity.bind(this);
36     this.handleEditCancel = this.handleEditCancel.bind(this);
37   }
38
39   render() {
40     return (
41       <div>
42         {!this.state.showEdit 
43           ? this.sidebar()
44           : <CommunityForm 
45           community={this.props.community} 
46           onEdit={this.handleEditCommunity} 
47           onCancel={this.handleEditCancel} />
48         }
49       </div>
50     )
51   }
52
53   sidebar() {
54     let community = this.props.community;
55     return (
56       <div>
57         <h5 className="mb-0">{community.title}
58         {community.removed &&
59           <small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
60         }
61         {community.deleted &&
62           <small className="ml-2 text-muted font-italic"><T i18nKey="deleted">#</T></small>
63         }
64       </h5>
65       <Link className="text-muted" to={`/c/${community.name}`}>/c/{community.name}</Link>
66       <ul class="list-inline mb-1 text-muted small font-weight-bold"> 
67         {this.canMod && 
68           <>
69             <li className="list-inline-item">
70               <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
71             </li>
72             {this.amCreator && 
73               <li className="list-inline-item">
74                 <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
75                   {!community.deleted ? i18n.t('delete') : i18n.t('restore')}
76                 </span>
77               </li>
78             }
79           </>
80         }
81         {this.canAdmin &&
82           <li className="list-inline-item">
83             {!this.props.community.removed ? 
84             <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
85             <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
86             }
87           </li>
88
89         }
90       </ul>
91       {this.state.showRemoveDialog && 
92         <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
93           <div class="form-group row">
94             <label class="col-form-label"><T i18nKey="reason">#</T></label>
95             <input type="text" class="form-control mr-2" placeholder={i18n.t('optional')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
96           </div>
97           {/* TODO hold off on expires for now */}
98           {/* <div class="form-group row"> */}
99           {/*   <label class="col-form-label">Expires</label> */}
100           {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
101           {/* </div> */}
102           <div class="form-group row">
103             <button type="submit" class="btn btn-secondary"><T i18nKey="remove_community">#</T></button>
104           </div>
105         </form>
106       }
107       <ul class="my-1 list-inline">
108         <li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
109         <li className="list-inline-item badge badge-light"><T i18nKey="number_of_subscribers" interpolation={{count: community.number_of_subscribers}}>#</T></li>
110         <li className="list-inline-item badge badge-light"><T i18nKey="number_of_posts" interpolation={{count: community.number_of_posts}}>#</T></li>
111         <li className="list-inline-item badge badge-light"><T i18nKey="number_of_comments" interpolation={{count: community.number_of_comments}}>#</T></li>
112         <li className="list-inline-item"><Link className="badge badge-light" to={`/modlog/community/${this.props.community.id}`}><T i18nKey="modlog">#</T></Link></li>
113       </ul>
114       <ul class="list-inline small"> 
115         <li class="list-inline-item">{i18n.t('mods')}: </li>
116         {this.props.moderators.map(mod =>
117           <li class="list-inline-item"><Link class="text-info" to={`/u/${mod.user_name}`}>{mod.user_name}</Link></li>
118         )}
119       </ul>
120       <Link class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted || community.removed) && 'no-click'}`}
121           to={`/create_post/c/${community.name}`}><T i18nKey="create_a_post">#</T></Link>
122       <div>
123         {community.subscribed 
124           ? <button class="btn btn-sm btn-secondary btn-block mb-3" onClick={linkEvent(community.id, this.handleUnsubscribe)}><T i18nKey="unsubscribe">#</T></button>
125           : <button class="btn btn-sm btn-secondary btn-block mb-3" onClick={linkEvent(community.id, this.handleSubscribe)}><T i18nKey="subscribe">#</T></button>
126         }
127       </div>
128       {community.description && 
129         <div>
130           <hr />
131           <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />
132           <hr />
133         </div>
134       }
135     </div>
136     );
137   }
138
139   handleEditClick(i: Sidebar) {
140     i.state.showEdit = true;
141     i.setState(i.state);
142   }
143
144   handleEditCommunity() {
145     this.state.showEdit = false;
146     this.setState(this.state);
147   }
148
149   handleEditCancel() {
150     this.state.showEdit = false;
151     this.setState(this.state);
152   }
153
154   handleDeleteClick(i: Sidebar) {
155     event.preventDefault();
156     let deleteForm: CommunityFormI = {
157       name: i.props.community.name,
158       title: i.props.community.title,
159       category_id: i.props.community.category_id,
160       edit_id: i.props.community.id,
161       deleted: !i.props.community.deleted,
162       auth: null,
163     };
164     WebSocketService.Instance.editCommunity(deleteForm);
165   }
166
167   handleUnsubscribe(communityId: number) {
168     let form: FollowCommunityForm = {
169       community_id: communityId,
170       follow: false
171     };
172     WebSocketService.Instance.followCommunity(form);
173   }
174
175   handleSubscribe(communityId: number) {
176     let form: FollowCommunityForm = {
177       community_id: communityId,
178       follow: true
179     };
180     WebSocketService.Instance.followCommunity(form);
181   }
182
183   private get amCreator(): boolean {
184     return this.props.community.creator_id == UserService.Instance.user.id;
185   }
186
187   get canMod(): boolean {
188     return UserService.Instance.user && this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
189   }
190
191   get canAdmin(): boolean {
192     return UserService.Instance.user && this.props.admins.map(a => a.id).includes(UserService.Instance.user.id);
193   }
194
195   handleModRemoveShow(i: Sidebar) {
196     i.state.showRemoveDialog = true;
197     i.setState(i.state);
198   }
199
200   handleModRemoveReasonChange(i: Sidebar, event: any) {
201     i.state.removeReason = event.target.value;
202     i.setState(i.state);
203   }
204
205   handleModRemoveExpiresChange(i: Sidebar, event: any) {
206     console.log(event.target.value);
207     i.state.removeExpires = event.target.value;
208     i.setState(i.state);
209   }
210
211   handleModRemoveSubmit(i: Sidebar) {
212     event.preventDefault();
213     let deleteForm: CommunityFormI = {
214       name: i.props.community.name,
215       title: i.props.community.title,
216       category_id: i.props.community.category_id,
217       edit_id: i.props.community.id,
218       removed: !i.props.community.removed,
219       reason: i.state.removeReason,
220       expires: getUnixTime(i.state.removeExpires),
221       auth: null,
222     };
223     WebSocketService.Instance.editCommunity(deleteForm);
224
225     i.state.showRemoveDialog = false;
226     i.setState(i.state);
227   }
228 }