]> Untitled Git - lemmy.git/blob - ui/src/components/sidebar.tsx
Cross posting working.
[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         <div class="card border-secondary mb-3">
58           <div class="card-body">
59             <h5 className="mb-0">
60               <span>{community.title}</span>
61               {community.removed &&
62                 <small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
63               }
64               {community.deleted &&
65                 <small className="ml-2 text-muted font-italic"><T i18nKey="deleted">#</T></small>
66               }
67             </h5>
68             <Link className="text-muted" to={`/c/${community.name}`}>/c/{community.name}</Link>
69             <ul class="list-inline mb-1 text-muted small font-weight-bold"> 
70               {this.canMod && 
71                 <>
72                   <li className="list-inline-item">
73                     <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
74                   </li>
75                   {this.amCreator && 
76                     <li className="list-inline-item">
77                       <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
78                         {!community.deleted ? i18n.t('delete') : i18n.t('restore')}
79                       </span>
80                     </li>
81                   }
82                 </>
83               }
84               {this.canAdmin &&
85                 <li className="list-inline-item">
86                   {!this.props.community.removed ? 
87                   <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
88                   <span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
89                   }
90                 </li>
91
92               }
93             </ul>
94             {this.state.showRemoveDialog && 
95               <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
96                 <div class="form-group row">
97                   <label class="col-form-label"><T i18nKey="reason">#</T></label>
98                   <input type="text" class="form-control mr-2" placeholder={i18n.t('optional')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
99                 </div>
100                 {/* TODO hold off on expires for now */}
101                 {/* <div class="form-group row"> */}
102                   {/*   <label class="col-form-label">Expires</label> */}
103                   {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
104                   {/* </div> */}
105                   <div class="form-group row">
106                     <button type="submit" class="btn btn-secondary"><T i18nKey="remove_community">#</T></button>
107                   </div>
108                 </form>
109             }
110             <ul class="my-1 list-inline">
111               <li className="list-inline-item"><Link className="badge badge-secondary" to="/communities">{community.category_name}</Link></li>
112               <li className="list-inline-item badge badge-secondary"><T i18nKey="number_of_subscribers" interpolation={{count: community.number_of_subscribers}}>#</T></li>
113               <li className="list-inline-item badge badge-secondary"><T i18nKey="number_of_posts" interpolation={{count: community.number_of_posts}}>#</T></li>
114               <li className="list-inline-item badge badge-secondary"><T i18nKey="number_of_comments" interpolation={{count: community.number_of_comments}}>#</T></li>
115               <li className="list-inline-item"><Link className="badge badge-secondary" to={`/modlog/community/${this.props.community.id}`}><T i18nKey="modlog">#</T></Link></li>
116             </ul>
117             <ul class="list-inline small"> 
118               <li class="list-inline-item">{i18n.t('mods')}: </li>
119               {this.props.moderators.map(mod =>
120                 <li class="list-inline-item"><Link class="text-info" to={`/u/${mod.user_name}`}>{mod.user_name}</Link></li>
121               )}
122             </ul>
123             <Link class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted || community.removed) && 'no-click'}`}
124             to={`/create_post?community=${community.name}`}><T i18nKey="create_a_post">#</T></Link>
125           <div>
126             {community.subscribed 
127               ? <button class="btn btn-sm btn-secondary btn-block" onClick={linkEvent(community.id, this.handleUnsubscribe)}><T i18nKey="unsubscribe">#</T></button>
128               : <button class="btn btn-sm btn-secondary btn-block" onClick={linkEvent(community.id, this.handleSubscribe)}><T i18nKey="subscribe">#</T></button>
129             }
130           </div>
131         </div>
132         </div>
133         {community.description && 
134           <div class="card border-secondary">
135             <div class="card-body">
136               <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />
137             </div>
138           </div>
139         }
140         </div>
141     );
142   }
143
144   handleEditClick(i: Sidebar) {
145     i.state.showEdit = true;
146     i.setState(i.state);
147   }
148
149   handleEditCommunity() {
150     this.state.showEdit = false;
151     this.setState(this.state);
152   }
153
154   handleEditCancel() {
155     this.state.showEdit = false;
156     this.setState(this.state);
157   }
158
159   handleDeleteClick(i: Sidebar) {
160     event.preventDefault();
161     let deleteForm: CommunityFormI = {
162       name: i.props.community.name,
163       title: i.props.community.title,
164       category_id: i.props.community.category_id,
165       edit_id: i.props.community.id,
166       deleted: !i.props.community.deleted,
167       auth: null,
168     };
169     WebSocketService.Instance.editCommunity(deleteForm);
170   }
171
172   handleUnsubscribe(communityId: number) {
173     let form: FollowCommunityForm = {
174       community_id: communityId,
175       follow: false
176     };
177     WebSocketService.Instance.followCommunity(form);
178   }
179
180   handleSubscribe(communityId: number) {
181     let form: FollowCommunityForm = {
182       community_id: communityId,
183       follow: true
184     };
185     WebSocketService.Instance.followCommunity(form);
186   }
187
188   private get amCreator(): boolean {
189     return this.props.community.creator_id == UserService.Instance.user.id;
190   }
191
192   get canMod(): boolean {
193     return UserService.Instance.user && this.props.moderators.map(m => m.user_id).includes(UserService.Instance.user.id);
194   }
195
196   get canAdmin(): boolean {
197     return UserService.Instance.user && this.props.admins.map(a => a.id).includes(UserService.Instance.user.id);
198   }
199
200   handleModRemoveShow(i: Sidebar) {
201     i.state.showRemoveDialog = true;
202     i.setState(i.state);
203   }
204
205   handleModRemoveReasonChange(i: Sidebar, event: any) {
206     i.state.removeReason = event.target.value;
207     i.setState(i.state);
208   }
209
210   handleModRemoveExpiresChange(i: Sidebar, event: any) {
211     console.log(event.target.value);
212     i.state.removeExpires = event.target.value;
213     i.setState(i.state);
214   }
215
216   handleModRemoveSubmit(i: Sidebar) {
217     event.preventDefault();
218     let deleteForm: CommunityFormI = {
219       name: i.props.community.name,
220       title: i.props.community.title,
221       category_id: i.props.community.category_id,
222       edit_id: i.props.community.id,
223       removed: !i.props.community.removed,
224       reason: i.state.removeReason,
225       expires: getUnixTime(i.state.removeExpires),
226       auth: null,
227     };
228     WebSocketService.Instance.editCommunity(deleteForm);
229
230     i.state.showRemoveDialog = false;
231     i.setState(i.state);
232   }
233 }