1 import { Component, InfernoNode, linkEvent } from "inferno";
2 import { T } from "inferno-i18next-dess";
3 import { Link } from "inferno-router";
7 CommunityModeratorView,
16 } from "lemmy-js-client";
17 import { i18n } from "../../i18next";
18 import { UserService } from "../../services";
28 import { Badges } from "../common/badges";
29 import { BannerIconHeader } from "../common/banner-icon-header";
30 import { Icon, PurgeWarning, Spinner } from "../common/icon";
31 import { CommunityForm } from "../community/community-form";
32 import { CommunityLink } from "../community/community-link";
33 import { PersonListing } from "../person/person-listing";
35 interface SidebarProps {
36 community_view: CommunityView;
37 moderators: CommunityModeratorView[];
39 allLanguages: Language[];
40 siteLanguages: number[];
41 communityLanguages?: number[];
45 onDeleteCommunity(form: DeleteCommunity): void;
46 onRemoveCommunity(form: RemoveCommunity): void;
47 onLeaveModTeam(form: AddModToCommunity): void;
48 onFollowCommunity(form: FollowCommunity): void;
49 onBlockCommunity(form: BlockCommunity): void;
50 onPurgeCommunity(form: PurgeCommunity): void;
51 onEditCommunity(form: EditCommunity): void;
54 interface SidebarState {
55 removeReason?: string;
56 removeExpires?: string;
58 showRemoveDialog: boolean;
59 showPurgeDialog: boolean;
61 showConfirmLeaveModTeam: boolean;
62 deleteCommunityLoading: boolean;
63 removeCommunityLoading: boolean;
64 leaveModTeamLoading: boolean;
65 followCommunityLoading: boolean;
66 purgeCommunityLoading: boolean;
69 export class Sidebar extends Component<SidebarProps, SidebarState> {
70 state: SidebarState = {
72 showRemoveDialog: false,
73 showPurgeDialog: false,
74 showConfirmLeaveModTeam: false,
75 deleteCommunityLoading: false,
76 removeCommunityLoading: false,
77 leaveModTeamLoading: false,
78 followCommunityLoading: false,
79 purgeCommunityLoading: false,
82 constructor(props: any, context: any) {
83 super(props, context);
84 this.handleEditCancel = this.handleEditCancel.bind(this);
87 componentWillReceiveProps(
88 nextProps: Readonly<{ children?: InfernoNode } & SidebarProps>
90 if (this.props.moderators != nextProps.moderators) {
92 showConfirmLeaveModTeam: false,
96 if (this.props.community_view != nextProps.community_view) {
99 showPurgeDialog: false,
100 showRemoveDialog: false,
101 deleteCommunityLoading: false,
102 removeCommunityLoading: false,
103 leaveModTeamLoading: false,
104 followCommunityLoading: false,
105 purgeCommunityLoading: false,
113 {!this.state.showEdit ? (
117 community_view={this.props.community_view}
118 allLanguages={this.props.allLanguages}
119 siteLanguages={this.props.siteLanguages}
120 communityLanguages={this.props.communityLanguages}
121 onUpsertCommunity={this.props.onEditCommunity}
122 onCancel={this.handleEditCancel}
123 enableNsfw={this.props.enableNsfw}
131 const myUSerInfo = UserService.Instance.myUserInfo;
132 const { name, actor_id } = this.props.community_view.community;
134 <aside className="mb-3">
135 <div id="sidebarContainer">
136 <section id="sidebarMain" className="card border-secondary mb-3">
137 <div className="card-body">
138 {this.communityTitle()}
139 {this.props.editable && this.adminButtons()}
140 {myUSerInfo && this.subscribe()}
141 {this.canPost && this.createPost()}
142 {myUSerInfo && this.blockCommunity()}
144 <div className="alert alert-info" role="alert">
146 i18nKey="community_not_logged_in_alert"
149 instance: hostname(actor_id),
152 #<code className="user-select-all">#</code>#
158 <section id="sidebarInfo" className="card border-secondary mb-3">
159 <div className="card-body">
162 communityId={this.props.community_view.community.id}
163 counts={this.props.community_view.counts}
174 const community = this.props.community_view.community;
175 const subscribed = this.props.community_view.subscribed;
178 <h5 className="mb-0">
179 {this.props.showIcon && !community.removed && (
180 <BannerIconHeader icon={community.icon} banner={community.banner} />
182 <span className="mr-2">
183 <CommunityLink community={community} hideAvatar />
185 {subscribed === "Subscribed" && (
187 className="btn btn-secondary btn-sm mr-2"
188 onClick={linkEvent(this, this.handleUnfollowCommunity)}
190 {this.state.followCommunityLoading ? (
194 <Icon icon="check" classes="icon-inline text-success mr-1" />
200 {subscribed === "Pending" && (
202 className="btn btn-warning mr-2"
203 onClick={linkEvent(this, this.handleUnfollowCommunity)}
205 {this.state.followCommunityLoading ? (
208 i18n.t("subscribe_pending")
212 {community.removed && (
213 <small className="mr-2 text-muted font-italic">
217 {community.deleted && (
218 <small className="mr-2 text-muted font-italic">
223 <small className="mr-2 text-muted font-italic">
229 community={community}
241 <ul className="list-inline small">
242 <li className="list-inline-item">{i18n.t("mods")}: </li>
243 {this.props.moderators.map(mod => (
244 <li key={mod.moderator.id} className="list-inline-item">
245 <PersonListing person={mod.moderator} />
253 const cv = this.props.community_view;
256 className={`btn btn-secondary btn-block mb-2 ${
257 cv.community.deleted || cv.community.removed ? "no-click" : ""
259 to={`/create_post?communityId=${cv.community.id}`}
261 {i18n.t("create_a_post")}
267 const community_view = this.props.community_view;
269 <div className="mb-2">
270 {community_view.subscribed == "NotSubscribed" && (
272 className="btn btn-secondary btn-block"
273 onClick={linkEvent(this, this.handleFollowCommunity)}
275 {this.state.followCommunityLoading ? (
287 const { subscribed, blocked } = this.props.community_view;
290 <div className="mb-2">
291 {subscribed == "NotSubscribed" && (
293 className="btn btn-danger btn-block"
294 onClick={linkEvent(this, this.handleBlockCommunity)}
296 {i18n.t(blocked ? "unblock_community" : "block_community")}
304 const desc = this.props.community_view.community.description;
307 <div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
313 const community_view = this.props.community_view;
316 <ul className="list-inline mb-1 text-muted font-weight-bold">
317 {amMod(this.props.moderators) && (
319 <li className="list-inline-item-action">
321 className="btn btn-link text-muted d-inline-block"
322 onClick={linkEvent(this, this.handleEditClick)}
323 data-tippy-content={i18n.t("edit")}
324 aria-label={i18n.t("edit")}
326 <Icon icon="edit" classes="icon-inline" />
329 {!amTopMod(this.props.moderators) &&
330 (!this.state.showConfirmLeaveModTeam ? (
331 <li className="list-inline-item-action">
333 className="btn btn-link text-muted d-inline-block"
336 this.handleShowConfirmLeaveModTeamClick
339 {i18n.t("leave_mod_team")}
344 <li className="list-inline-item-action">
345 {i18n.t("are_you_sure")}
347 <li className="list-inline-item-action">
349 className="btn btn-link text-muted d-inline-block"
350 onClick={linkEvent(this, this.handleLeaveModTeam)}
355 <li className="list-inline-item-action">
357 className="btn btn-link text-muted d-inline-block"
360 this.handleCancelLeaveModTeamClick
368 {amTopMod(this.props.moderators) && (
369 <li className="list-inline-item-action">
371 className="btn btn-link text-muted d-inline-block"
372 onClick={linkEvent(this, this.handleDeleteCommunity)}
374 !community_view.community.deleted
379 !community_view.community.deleted
384 {this.state.deleteCommunityLoading ? (
389 classes={`icon-inline ${
390 community_view.community.deleted && "text-danger"
400 <li className="list-inline-item">
401 {!this.props.community_view.community.removed ? (
403 className="btn btn-link text-muted d-inline-block"
404 onClick={linkEvent(this, this.handleModRemoveShow)}
410 className="btn btn-link text-muted d-inline-block"
411 onClick={linkEvent(this, this.handleRemoveCommunity)}
413 {this.state.removeCommunityLoading ? (
421 className="btn btn-link text-muted d-inline-block"
422 onClick={linkEvent(this, this.handlePurgeCommunityShow)}
423 aria-label={i18n.t("purge_community")}
425 {i18n.t("purge_community")}
430 {this.state.showRemoveDialog && (
431 <form onSubmit={linkEvent(this, this.handleRemoveCommunity)}>
432 <div className="form-group">
433 <label className="col-form-label" htmlFor="remove-reason">
439 className="form-control mr-2"
440 placeholder={i18n.t("optional")}
441 value={this.state.removeReason}
442 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
445 {/* TODO hold off on expires for now */}
446 {/* <div class="form-group row"> */}
447 {/* <label class="col-form-label">Expires</label> */}
448 {/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
450 <div className="form-group">
451 <button type="submit" className="btn btn-secondary">
452 {this.state.removeCommunityLoading ? (
455 i18n.t("remove_community")
461 {this.state.showPurgeDialog && (
462 <form onSubmit={linkEvent(this, this.handlePurgeCommunity)}>
463 <div className="form-group">
466 <div className="form-group">
467 <label className="sr-only" htmlFor="purge-reason">
473 className="form-control mr-2"
474 placeholder={i18n.t("reason")}
475 value={this.state.purgeReason}
476 onInput={linkEvent(this, this.handlePurgeReasonChange)}
479 <div className="form-group">
480 {this.state.purgeCommunityLoading ? (
485 className="btn btn-secondary"
486 aria-label={i18n.t("purge_community")}
488 {i18n.t("purge_community")}
498 handleEditClick(i: Sidebar) {
499 i.setState({ showEdit: true });
503 this.setState({ showEdit: false });
506 handleShowConfirmLeaveModTeamClick(i: Sidebar) {
507 i.setState({ showConfirmLeaveModTeam: true });
510 handleCancelLeaveModTeamClick(i: Sidebar) {
511 i.setState({ showConfirmLeaveModTeam: false });
514 get canPost(): boolean {
516 !this.props.community_view.community.posting_restricted_to_mods ||
517 amMod(this.props.moderators) ||
522 handleModRemoveShow(i: Sidebar) {
523 i.setState({ showRemoveDialog: true });
526 handleModRemoveReasonChange(i: Sidebar, event: any) {
527 i.setState({ removeReason: event.target.value });
530 handleModRemoveExpiresChange(i: Sidebar, event: any) {
531 i.setState({ removeExpires: event.target.value });
534 handlePurgeCommunityShow(i: Sidebar) {
535 i.setState({ showPurgeDialog: true, showRemoveDialog: false });
538 handlePurgeReasonChange(i: Sidebar, event: any) {
539 i.setState({ purgeReason: event.target.value });
542 // TODO Do we need two of these?
543 handleUnfollowCommunity(i: Sidebar) {
544 i.setState({ followCommunityLoading: true });
545 i.props.onFollowCommunity({
546 community_id: i.props.community_view.community.id,
548 auth: myAuthRequired(),
552 handleFollowCommunity(i: Sidebar) {
553 i.setState({ followCommunityLoading: true });
554 i.props.onFollowCommunity({
555 community_id: i.props.community_view.community.id,
557 auth: myAuthRequired(),
561 handleBlockCommunity(i: Sidebar) {
562 const { community, blocked } = i.props.community_view;
564 i.props.onBlockCommunity({
565 community_id: community.id,
567 auth: myAuthRequired(),
571 handleLeaveModTeam(i: Sidebar) {
572 const myId = UserService.Instance.myUserInfo?.local_user_view.person.id;
574 i.setState({ leaveModTeamLoading: true });
575 i.props.onLeaveModTeam({
576 community_id: i.props.community_view.community.id,
579 auth: myAuthRequired(),
584 handleDeleteCommunity(i: Sidebar) {
585 i.setState({ deleteCommunityLoading: true });
586 i.props.onDeleteCommunity({
587 community_id: i.props.community_view.community.id,
588 deleted: !i.props.community_view.community.deleted,
589 auth: myAuthRequired(),
593 handleRemoveCommunity(i: Sidebar, event: any) {
594 event.preventDefault();
595 i.setState({ removeCommunityLoading: true });
596 i.props.onRemoveCommunity({
597 community_id: i.props.community_view.community.id,
598 removed: !i.props.community_view.community.removed,
599 reason: i.state.removeReason,
600 expires: getUnixTime(i.state.removeExpires), // TODO fix this
601 auth: myAuthRequired(),
605 handlePurgeCommunity(i: Sidebar, event: any) {
606 event.preventDefault();
607 i.setState({ purgeCommunityLoading: true });
608 i.props.onPurgeCommunity({
609 community_id: i.props.community_view.community.id,
610 reason: i.state.purgeReason,
611 auth: myAuthRequired(),