1 import { Component, linkEvent } from 'inferno';
2 import { Link } from 'inferno-router';
5 CommunityModeratorView,
12 } from 'lemmy-js-client';
13 import { WebSocketService, UserService } from '../services';
14 import { mdToHtml, getUnixTime, wsClient, authField } from '../utils';
15 import { CommunityForm } from './community-form';
16 import { UserListing } from './user-listing';
17 import { CommunityLink } from './community-link';
18 import { BannerIconHeader } from './banner-icon-header';
19 import { Icon } from './icon';
20 import { i18n } from '../i18next';
22 interface SidebarProps {
23 community_view: CommunityView;
24 categories: Category[];
25 moderators: CommunityModeratorView[];
26 admins: UserViewSafe[];
32 interface SidebarState {
34 showRemoveDialog: boolean;
36 removeExpires: string;
37 showConfirmLeaveModTeam: boolean;
40 export class Sidebar extends Component<SidebarProps, SidebarState> {
41 private emptyState: SidebarState = {
43 showRemoveDialog: false,
46 showConfirmLeaveModTeam: false,
49 constructor(props: any, context: any) {
50 super(props, context);
51 this.state = this.emptyState;
52 this.handleEditCommunity = this.handleEditCommunity.bind(this);
53 this.handleEditCancel = this.handleEditCancel.bind(this);
59 {!this.state.showEdit ? (
63 categories={this.props.categories}
64 community_view={this.props.community_view}
65 onEdit={this.handleEditCommunity}
66 onCancel={this.handleEditCancel}
67 enableNsfw={this.props.enableNsfw}
77 <div class="card border-secondary mb-3">
78 <div class="card-body">
79 {this.communityTitle()}
85 <div class="card border-secondary mb-3">
86 <div class="card-body">
97 let community = this.props.community_view.community;
98 let subscribed = this.props.community_view.subscribed;
101 <h5 className="mb-0">
102 {this.props.showIcon && (
103 <BannerIconHeader icon={community.icon} banner={community.banner} />
105 <span class="mr-2">{community.title}</span>
108 class="btn btn-secondary btn-sm mr-2"
110 onClick={linkEvent(community.id, this.handleUnsubscribe)}
112 <Icon icon="check" classes="icon-inline text-success mr-1" />
116 {community.removed && (
117 <small className="mr-2 text-muted font-italic">
121 {community.deleted && (
122 <small className="mr-2 text-muted font-italic">
127 <small className="mr-2 text-muted font-italic">
133 community={community}
144 let community_view = this.props.community_view;
145 let counts = community_view.counts;
147 <ul class="my-1 list-inline">
148 <li className="list-inline-item badge badge-secondary">
149 {i18n.t('number_online', { count: this.props.online })}
152 className="list-inline-item badge badge-secondary pointer"
153 data-tippy-content={`${i18n.t('number_of_users', {
154 count: counts.users_active_day,
155 })} ${i18n.t('active_in_the_last')} ${i18n.t('day')}`}
157 {i18n.t('number_of_users', {
158 count: counts.users_active_day,
163 className="list-inline-item badge badge-secondary pointer"
164 data-tippy-content={`${i18n.t('number_of_users', {
165 count: counts.users_active_week,
166 })} ${i18n.t('active_in_the_last')} ${i18n.t('week')}`}
168 {i18n.t('number_of_users', {
169 count: counts.users_active_week,
174 className="list-inline-item badge badge-secondary pointer"
175 data-tippy-content={`${i18n.t('number_of_users', {
176 count: counts.users_active_month,
177 })} ${i18n.t('active_in_the_last')} ${i18n.t('month')}`}
179 {i18n.t('number_of_users', {
180 count: counts.users_active_month,
185 className="list-inline-item badge badge-secondary pointer"
186 data-tippy-content={`${i18n.t('number_of_users', {
187 count: counts.users_active_half_year,
188 })} ${i18n.t('active_in_the_last')} ${i18n.t('number_of_months', {
192 {i18n.t('number_of_users', {
193 count: counts.users_active_half_year,
195 / {i18n.t('number_of_months', { count: 6 })}
197 <li className="list-inline-item badge badge-secondary">
198 {i18n.t('number_of_subscribers', {
199 count: counts.subscribers,
202 <li className="list-inline-item badge badge-secondary">
203 {i18n.t('number_of_posts', {
207 <li className="list-inline-item badge badge-secondary">
208 {i18n.t('number_of_comments', {
209 count: counts.comments,
212 <li className="list-inline-item">
213 <Link className="badge badge-secondary" to="/communities">
214 {community_view.category.name}
217 <li className="list-inline-item">
219 className="badge badge-secondary"
220 to={`/modlog/community/${this.props.community_view.community.id}`}
231 <ul class="list-inline small">
232 <li class="list-inline-item">{i18n.t('mods')}: </li>
233 {this.props.moderators.map(mod => (
234 <li class="list-inline-item">
235 <UserListing user={mod.moderator} />
243 let community_view = this.props.community_view;
245 community_view.subscribed && (
247 className={`btn btn-secondary btn-block mb-2 ${
248 community_view.community.deleted || community_view.community.removed
252 to={`/create_post?community_id=${community_view.community.id}`}
254 {i18n.t('create_a_post')}
261 let community_view = this.props.community_view;
264 {!community_view.subscribed && (
266 class="btn btn-secondary btn-block"
269 community_view.community.id,
273 {i18n.t('subscribe')}
281 let description = this.props.community_view.community.description;
286 dangerouslySetInnerHTML={mdToHtml(description)}
293 let community_view = this.props.community_view;
296 <ul class="list-inline mb-1 text-muted font-weight-bold">
299 <li className="list-inline-item-action">
303 onClick={linkEvent(this, this.handleEditClick)}
304 data-tippy-content={i18n.t('edit')}
305 aria-label={i18n.t('edit')}
307 <Icon icon="edit" classes="icon-inline" />
311 (!this.state.showConfirmLeaveModTeam ? (
312 <li className="list-inline-item-action">
318 this.handleShowConfirmLeaveModTeamClick
321 {i18n.t('leave_mod_team')}
326 <li className="list-inline-item-action">
327 {i18n.t('are_you_sure')}
329 <li className="list-inline-item-action">
333 onClick={linkEvent(this, this.handleLeaveModTeamClick)}
338 <li className="list-inline-item-action">
344 this.handleCancelLeaveModTeamClick
353 <li className="list-inline-item-action">
356 onClick={linkEvent(this, this.handleDeleteClick)}
358 !community_view.community.deleted
363 !community_view.community.deleted
370 classes={`icon-inline ${
371 community_view.community.deleted && 'text-danger'
380 <li className="list-inline-item">
381 {!this.props.community_view.community.removed ? (
385 onClick={linkEvent(this, this.handleModRemoveShow)}
393 onClick={linkEvent(this, this.handleModRemoveSubmit)}
401 {this.state.showRemoveDialog && (
402 <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
403 <div class="form-group row">
404 <label class="col-form-label" htmlFor="remove-reason">
410 class="form-control mr-2"
411 placeholder={i18n.t('optional')}
412 value={this.state.removeReason}
413 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
416 {/* TODO hold off on expires for now */}
417 {/* <div class="form-group row"> */}
418 {/* <label class="col-form-label">Expires</label> */}
419 {/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
421 <div class="form-group row">
422 <button type="submit" class="btn btn-secondary">
423 {i18n.t('remove_community')}
432 handleEditClick(i: Sidebar) {
433 i.state.showEdit = true;
437 handleEditCommunity() {
438 this.state.showEdit = false;
439 this.setState(this.state);
443 this.state.showEdit = false;
444 this.setState(this.state);
447 handleDeleteClick(i: Sidebar, event: any) {
448 event.preventDefault();
449 let deleteForm: DeleteCommunity = {
450 community_id: i.props.community_view.community.id,
451 deleted: !i.props.community_view.community.deleted,
454 WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
457 handleShowConfirmLeaveModTeamClick(i: Sidebar) {
458 i.state.showConfirmLeaveModTeam = true;
462 handleLeaveModTeamClick(i: Sidebar) {
463 let form: AddModToCommunity = {
464 user_id: UserService.Instance.user.id,
465 community_id: i.props.community_view.community.id,
469 WebSocketService.Instance.send(wsClient.addModToCommunity(form));
470 i.state.showConfirmLeaveModTeam = false;
474 handleCancelLeaveModTeamClick(i: Sidebar) {
475 i.state.showConfirmLeaveModTeam = false;
479 handleUnsubscribe(communityId: number, event: any) {
480 event.preventDefault();
481 let form: FollowCommunity = {
482 community_id: communityId,
486 WebSocketService.Instance.send(wsClient.followCommunity(form));
489 handleSubscribe(communityId: number, event: any) {
490 event.preventDefault();
491 let form: FollowCommunity = {
492 community_id: communityId,
496 WebSocketService.Instance.send(wsClient.followCommunity(form));
499 private get amCreator(): boolean {
500 return this.props.community_view.creator.id == UserService.Instance.user.id;
503 get canMod(): boolean {
505 UserService.Instance.user &&
506 this.props.moderators
507 .map(m => m.moderator.id)
508 .includes(UserService.Instance.user.id)
512 get canAdmin(): boolean {
514 UserService.Instance.user &&
517 .includes(UserService.Instance.user.id)
521 handleModRemoveShow(i: Sidebar) {
522 i.state.showRemoveDialog = true;
526 handleModRemoveReasonChange(i: Sidebar, event: any) {
527 i.state.removeReason = event.target.value;
531 handleModRemoveExpiresChange(i: Sidebar, event: any) {
532 console.log(event.target.value);
533 i.state.removeExpires = event.target.value;
537 handleModRemoveSubmit(i: Sidebar, event: any) {
538 event.preventDefault();
539 let removeForm: RemoveCommunity = {
540 community_id: i.props.community_view.community.id,
541 removed: !i.props.community_view.community.removed,
542 reason: i.state.removeReason,
543 expires: getUnixTime(i.state.removeExpires),
546 WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
548 i.state.showRemoveDialog = false;