1 import { amAdmin, amMod, amTopMod } from "@utils/roles";
2 import { Component, InfernoNode, linkEvent } from "inferno";
3 import { T } from "inferno-i18next-dess";
4 import { Link } from "inferno-router";
8 CommunityModeratorView,
17 } from "lemmy-js-client";
18 import { i18n } from "../../i18next";
19 import { UserService } from "../../services";
20 import { getUnixTime, hostname, mdToHtml, myAuthRequired } from "../../utils";
21 import { Badges } from "../common/badges";
22 import { BannerIconHeader } from "../common/banner-icon-header";
23 import { Icon, PurgeWarning, Spinner } from "../common/icon";
24 import { CommunityForm } from "../community/community-form";
25 import { CommunityLink } from "../community/community-link";
26 import { PersonListing } from "../person/person-listing";
28 interface SidebarProps {
29 community_view: CommunityView;
30 moderators: CommunityModeratorView[];
32 allLanguages: Language[];
33 siteLanguages: number[];
34 communityLanguages?: number[];
38 onDeleteCommunity(form: DeleteCommunity): void;
39 onRemoveCommunity(form: RemoveCommunity): void;
40 onLeaveModTeam(form: AddModToCommunity): void;
41 onFollowCommunity(form: FollowCommunity): void;
42 onBlockCommunity(form: BlockCommunity): void;
43 onPurgeCommunity(form: PurgeCommunity): void;
44 onEditCommunity(form: EditCommunity): void;
47 interface SidebarState {
48 removeReason?: string;
49 removeExpires?: string;
51 showRemoveDialog: boolean;
52 showPurgeDialog: boolean;
54 showConfirmLeaveModTeam: boolean;
55 deleteCommunityLoading: boolean;
56 removeCommunityLoading: boolean;
57 leaveModTeamLoading: boolean;
58 followCommunityLoading: boolean;
59 purgeCommunityLoading: boolean;
62 export class Sidebar extends Component<SidebarProps, SidebarState> {
63 state: SidebarState = {
65 showRemoveDialog: false,
66 showPurgeDialog: false,
67 showConfirmLeaveModTeam: false,
68 deleteCommunityLoading: false,
69 removeCommunityLoading: false,
70 leaveModTeamLoading: false,
71 followCommunityLoading: false,
72 purgeCommunityLoading: false,
75 constructor(props: any, context: any) {
76 super(props, context);
77 this.handleEditCancel = this.handleEditCancel.bind(this);
80 componentWillReceiveProps(
81 nextProps: Readonly<{ children?: InfernoNode } & SidebarProps>
83 if (this.props.moderators != nextProps.moderators) {
85 showConfirmLeaveModTeam: false,
89 if (this.props.community_view != nextProps.community_view) {
92 showPurgeDialog: false,
93 showRemoveDialog: false,
94 deleteCommunityLoading: false,
95 removeCommunityLoading: false,
96 leaveModTeamLoading: false,
97 followCommunityLoading: false,
98 purgeCommunityLoading: false,
105 <div className="community-sidebar">
106 {!this.state.showEdit ? (
110 community_view={this.props.community_view}
111 allLanguages={this.props.allLanguages}
112 siteLanguages={this.props.siteLanguages}
113 communityLanguages={this.props.communityLanguages}
114 onUpsertCommunity={this.props.onEditCommunity}
115 onCancel={this.handleEditCancel}
116 enableNsfw={this.props.enableNsfw}
124 const myUSerInfo = UserService.Instance.myUserInfo;
125 const { name, actor_id } = this.props.community_view.community;
127 <aside className="mb-3">
128 <div id="sidebarContainer">
129 <section id="sidebarMain" className="card border-secondary mb-3">
130 <div className="card-body">
131 {this.communityTitle()}
132 {this.props.editable && this.adminButtons()}
133 {myUSerInfo && this.subscribe()}
134 {this.canPost && this.createPost()}
135 {myUSerInfo && this.blockCommunity()}
137 <div className="alert alert-info" role="alert">
139 i18nKey="community_not_logged_in_alert"
142 instance: hostname(actor_id),
145 #<code className="user-select-all">#</code>#
151 <section id="sidebarInfo" className="card border-secondary mb-3">
152 <div className="card-body">
155 communityId={this.props.community_view.community.id}
156 counts={this.props.community_view.counts}
167 const community = this.props.community_view.community;
168 const subscribed = this.props.community_view.subscribed;
171 <h5 className="mb-0">
172 {this.props.showIcon && !community.removed && (
173 <BannerIconHeader icon={community.icon} banner={community.banner} />
175 <span className="me-2">
176 <CommunityLink community={community} hideAvatar />
178 {subscribed === "Subscribed" && (
180 className="btn btn-secondary btn-sm me-2"
181 onClick={linkEvent(this, this.handleUnfollowCommunity)}
183 {this.state.followCommunityLoading ? (
187 <Icon icon="check" classes="icon-inline text-success me-1" />
193 {subscribed === "Pending" && (
195 className="btn btn-warning me-2"
196 onClick={linkEvent(this, this.handleUnfollowCommunity)}
198 {this.state.followCommunityLoading ? (
201 i18n.t("subscribe_pending")
205 {community.removed && (
206 <small className="me-2 text-muted font-italic">
210 {community.deleted && (
211 <small className="me-2 text-muted font-italic">
216 <small className="me-2 text-muted font-italic">
222 community={community}
234 <ul className="list-inline small">
235 <li className="list-inline-item">{i18n.t("mods")}: </li>
236 {this.props.moderators.map(mod => (
237 <li key={mod.moderator.id} className="list-inline-item">
238 <PersonListing person={mod.moderator} />
246 const cv = this.props.community_view;
249 className={`btn btn-secondary d-block mb-2 w-100 ${
250 cv.community.deleted || cv.community.removed ? "no-click" : ""
252 to={`/create_post?communityId=${cv.community.id}`}
254 {i18n.t("create_a_post")}
260 const community_view = this.props.community_view;
263 {community_view.subscribed == "NotSubscribed" && (
265 className="btn btn-secondary d-block mb-2 w-100"
266 onClick={linkEvent(this, this.handleFollowCommunity)}
268 {this.state.followCommunityLoading ? (
280 const { subscribed, blocked } = this.props.community_view;
284 {subscribed == "NotSubscribed" && (
286 className="btn btn-danger d-block mb-2 w-100"
287 onClick={linkEvent(this, this.handleBlockCommunity)}
289 {i18n.t(blocked ? "unblock_community" : "block_community")}
297 const desc = this.props.community_view.community.description;
300 <div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
306 const community_view = this.props.community_view;
309 <ul className="list-inline mb-1 text-muted font-weight-bold">
310 {amMod(this.props.moderators) && (
312 <li className="list-inline-item-action">
314 className="btn btn-link text-muted d-inline-block"
315 onClick={linkEvent(this, this.handleEditClick)}
316 data-tippy-content={i18n.t("edit")}
317 aria-label={i18n.t("edit")}
319 <Icon icon="edit" classes="icon-inline" />
322 {!amTopMod(this.props.moderators) &&
323 (!this.state.showConfirmLeaveModTeam ? (
324 <li className="list-inline-item-action">
326 className="btn btn-link text-muted d-inline-block"
329 this.handleShowConfirmLeaveModTeamClick
332 {i18n.t("leave_mod_team")}
337 <li className="list-inline-item-action">
338 {i18n.t("are_you_sure")}
340 <li className="list-inline-item-action">
342 className="btn btn-link text-muted d-inline-block"
343 onClick={linkEvent(this, this.handleLeaveModTeam)}
348 <li className="list-inline-item-action">
350 className="btn btn-link text-muted d-inline-block"
353 this.handleCancelLeaveModTeamClick
361 {amTopMod(this.props.moderators) && (
362 <li className="list-inline-item-action">
364 className="btn btn-link text-muted d-inline-block"
365 onClick={linkEvent(this, this.handleDeleteCommunity)}
367 !community_view.community.deleted
372 !community_view.community.deleted
377 {this.state.deleteCommunityLoading ? (
382 classes={`icon-inline ${
383 community_view.community.deleted && "text-danger"
393 <li className="list-inline-item">
394 {!this.props.community_view.community.removed ? (
396 className="btn btn-link text-muted d-inline-block"
397 onClick={linkEvent(this, this.handleModRemoveShow)}
403 className="btn btn-link text-muted d-inline-block"
404 onClick={linkEvent(this, this.handleRemoveCommunity)}
406 {this.state.removeCommunityLoading ? (
414 className="btn btn-link text-muted d-inline-block"
415 onClick={linkEvent(this, this.handlePurgeCommunityShow)}
416 aria-label={i18n.t("purge_community")}
418 {i18n.t("purge_community")}
423 {this.state.showRemoveDialog && (
424 <form onSubmit={linkEvent(this, this.handleRemoveCommunity)}>
425 <div className="input-group mb-3">
426 <label className="col-form-label" htmlFor="remove-reason">
432 className="form-control me-2"
433 placeholder={i18n.t("optional")}
434 value={this.state.removeReason}
435 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
438 {/* TODO hold off on expires for now */}
439 {/* <div class="mb-3 row"> */}
440 {/* <label class="col-form-label">Expires</label> */}
441 {/* <input type="date" class="form-control me-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
443 <div className="input-group mb-3">
444 <button type="submit" className="btn btn-secondary">
445 {this.state.removeCommunityLoading ? (
448 i18n.t("remove_community")
454 {this.state.showPurgeDialog && (
455 <form onSubmit={linkEvent(this, this.handlePurgeCommunity)}>
456 <div className="input-group mb-3">
459 <div className="input-group mb-3">
460 <label className="visually-hidden" htmlFor="purge-reason">
466 className="form-control me-2"
467 placeholder={i18n.t("reason")}
468 value={this.state.purgeReason}
469 onInput={linkEvent(this, this.handlePurgeReasonChange)}
472 <div className="input-group mb-3">
473 {this.state.purgeCommunityLoading ? (
478 className="btn btn-secondary"
479 aria-label={i18n.t("purge_community")}
481 {i18n.t("purge_community")}
491 handleEditClick(i: Sidebar) {
492 i.setState({ showEdit: true });
496 this.setState({ showEdit: false });
499 handleShowConfirmLeaveModTeamClick(i: Sidebar) {
500 i.setState({ showConfirmLeaveModTeam: true });
503 handleCancelLeaveModTeamClick(i: Sidebar) {
504 i.setState({ showConfirmLeaveModTeam: false });
507 get canPost(): boolean {
509 !this.props.community_view.community.posting_restricted_to_mods ||
510 amMod(this.props.moderators) ||
515 handleModRemoveShow(i: Sidebar) {
516 i.setState({ showRemoveDialog: true });
519 handleModRemoveReasonChange(i: Sidebar, event: any) {
520 i.setState({ removeReason: event.target.value });
523 handleModRemoveExpiresChange(i: Sidebar, event: any) {
524 i.setState({ removeExpires: event.target.value });
527 handlePurgeCommunityShow(i: Sidebar) {
528 i.setState({ showPurgeDialog: true, showRemoveDialog: false });
531 handlePurgeReasonChange(i: Sidebar, event: any) {
532 i.setState({ purgeReason: event.target.value });
535 // TODO Do we need two of these?
536 handleUnfollowCommunity(i: Sidebar) {
537 i.setState({ followCommunityLoading: true });
538 i.props.onFollowCommunity({
539 community_id: i.props.community_view.community.id,
541 auth: myAuthRequired(),
545 handleFollowCommunity(i: Sidebar) {
546 i.setState({ followCommunityLoading: true });
547 i.props.onFollowCommunity({
548 community_id: i.props.community_view.community.id,
550 auth: myAuthRequired(),
554 handleBlockCommunity(i: Sidebar) {
555 const { community, blocked } = i.props.community_view;
557 i.props.onBlockCommunity({
558 community_id: community.id,
560 auth: myAuthRequired(),
564 handleLeaveModTeam(i: Sidebar) {
565 const myId = UserService.Instance.myUserInfo?.local_user_view.person.id;
567 i.setState({ leaveModTeamLoading: true });
568 i.props.onLeaveModTeam({
569 community_id: i.props.community_view.community.id,
572 auth: myAuthRequired(),
577 handleDeleteCommunity(i: Sidebar) {
578 i.setState({ deleteCommunityLoading: true });
579 i.props.onDeleteCommunity({
580 community_id: i.props.community_view.community.id,
581 deleted: !i.props.community_view.community.deleted,
582 auth: myAuthRequired(),
586 handleRemoveCommunity(i: Sidebar, event: any) {
587 event.preventDefault();
588 i.setState({ removeCommunityLoading: true });
589 i.props.onRemoveCommunity({
590 community_id: i.props.community_view.community.id,
591 removed: !i.props.community_view.community.removed,
592 reason: i.state.removeReason,
593 expires: getUnixTime(i.state.removeExpires), // TODO fix this
594 auth: myAuthRequired(),
598 handlePurgeCommunity(i: Sidebar, event: any) {
599 event.preventDefault();
600 i.setState({ purgeCommunityLoading: true });
601 i.props.onPurgeCommunity({
602 community_id: i.props.community_view.community.id,
603 reason: i.state.purgeReason,
604 auth: myAuthRequired(),