1 import { myAuthRequired } from "@utils/app";
2 import { getUnixTime, hostname } from "@utils/helpers";
3 import { amAdmin, amMod, amTopMod } from "@utils/roles";
4 import { Component, InfernoNode, linkEvent } from "inferno";
5 import { T } from "inferno-i18next-dess";
6 import { Link } from "inferno-router";
10 CommunityModeratorView,
19 } from "lemmy-js-client";
20 import { i18n } from "../../i18next";
21 import { mdToHtml } from "../../markdown";
22 import { UserService } from "../../services";
23 import { Badges } from "../common/badges";
24 import { BannerIconHeader } from "../common/banner-icon-header";
25 import { Icon, PurgeWarning, Spinner } from "../common/icon";
26 import { CommunityForm } from "../community/community-form";
27 import { CommunityLink } from "../community/community-link";
28 import { PersonListing } from "../person/person-listing";
30 interface SidebarProps {
31 community_view: CommunityView;
32 moderators: CommunityModeratorView[];
34 allLanguages: Language[];
35 siteLanguages: number[];
36 communityLanguages?: number[];
40 onDeleteCommunity(form: DeleteCommunity): void;
41 onRemoveCommunity(form: RemoveCommunity): void;
42 onLeaveModTeam(form: AddModToCommunity): void;
43 onFollowCommunity(form: FollowCommunity): void;
44 onBlockCommunity(form: BlockCommunity): void;
45 onPurgeCommunity(form: PurgeCommunity): void;
46 onEditCommunity(form: EditCommunity): void;
49 interface SidebarState {
50 removeReason?: string;
51 removeExpires?: string;
53 showRemoveDialog: boolean;
54 showPurgeDialog: boolean;
56 showConfirmLeaveModTeam: boolean;
57 deleteCommunityLoading: boolean;
58 removeCommunityLoading: boolean;
59 leaveModTeamLoading: boolean;
60 followCommunityLoading: boolean;
61 purgeCommunityLoading: boolean;
64 export class Sidebar extends Component<SidebarProps, SidebarState> {
65 state: SidebarState = {
67 showRemoveDialog: false,
68 showPurgeDialog: false,
69 showConfirmLeaveModTeam: false,
70 deleteCommunityLoading: false,
71 removeCommunityLoading: false,
72 leaveModTeamLoading: false,
73 followCommunityLoading: false,
74 purgeCommunityLoading: false,
77 constructor(props: any, context: any) {
78 super(props, context);
79 this.handleEditCancel = this.handleEditCancel.bind(this);
82 componentWillReceiveProps(
83 nextProps: Readonly<{ children?: InfernoNode } & SidebarProps>
85 if (this.props.moderators != nextProps.moderators) {
87 showConfirmLeaveModTeam: false,
91 if (this.props.community_view != nextProps.community_view) {
94 showPurgeDialog: false,
95 showRemoveDialog: false,
96 deleteCommunityLoading: false,
97 removeCommunityLoading: false,
98 leaveModTeamLoading: false,
99 followCommunityLoading: false,
100 purgeCommunityLoading: false,
107 <div className="community-sidebar">
108 {!this.state.showEdit ? (
112 community_view={this.props.community_view}
113 allLanguages={this.props.allLanguages}
114 siteLanguages={this.props.siteLanguages}
115 communityLanguages={this.props.communityLanguages}
116 onUpsertCommunity={this.props.onEditCommunity}
117 onCancel={this.handleEditCancel}
118 enableNsfw={this.props.enableNsfw}
126 const myUSerInfo = UserService.Instance.myUserInfo;
127 const { name, actor_id } = this.props.community_view.community;
129 <aside className="mb-3">
130 <div id="sidebarContainer">
131 <section id="sidebarMain" className="card border-secondary mb-3">
132 <div className="card-body">
133 {this.communityTitle()}
134 {this.props.editable && this.adminButtons()}
135 {myUSerInfo && this.subscribe()}
136 {this.canPost && this.createPost()}
137 {myUSerInfo && this.blockCommunity()}
139 <div className="alert alert-info" role="alert">
141 i18nKey="community_not_logged_in_alert"
144 instance: hostname(actor_id),
147 #<code className="user-select-all">#</code>#
153 <section id="sidebarInfo" className="card border-secondary mb-3">
154 <div className="card-body">
157 communityId={this.props.community_view.community.id}
158 counts={this.props.community_view.counts}
169 const community = this.props.community_view.community;
170 const subscribed = this.props.community_view.subscribed;
173 <h5 className="mb-0">
174 {this.props.showIcon && !community.removed && (
175 <BannerIconHeader icon={community.icon} banner={community.banner} />
177 <span className="me-2">
178 <CommunityLink community={community} hideAvatar />
180 {subscribed === "Subscribed" && (
182 className="btn btn-secondary btn-sm me-2"
183 onClick={linkEvent(this, this.handleUnfollowCommunity)}
185 {this.state.followCommunityLoading ? (
189 <Icon icon="check" classes="icon-inline text-success me-1" />
195 {subscribed === "Pending" && (
197 className="btn btn-warning me-2"
198 onClick={linkEvent(this, this.handleUnfollowCommunity)}
200 {this.state.followCommunityLoading ? (
203 i18n.t("subscribe_pending")
207 {community.removed && (
208 <small className="me-2 text-muted font-italic">
212 {community.deleted && (
213 <small className="me-2 text-muted font-italic">
218 <small className="me-2 text-muted font-italic">
224 community={community}
236 <ul className="list-inline small">
237 <li className="list-inline-item">{i18n.t("mods")}: </li>
238 {this.props.moderators.map(mod => (
239 <li key={mod.moderator.id} className="list-inline-item">
240 <PersonListing person={mod.moderator} />
248 const cv = this.props.community_view;
251 className={`btn btn-secondary d-block mb-2 w-100 ${
252 cv.community.deleted || cv.community.removed ? "no-click" : ""
254 to={`/create_post?communityId=${cv.community.id}`}
256 {i18n.t("create_a_post")}
262 const community_view = this.props.community_view;
265 {community_view.subscribed == "NotSubscribed" && (
267 className="btn btn-secondary d-block mb-2 w-100"
268 onClick={linkEvent(this, this.handleFollowCommunity)}
270 {this.state.followCommunityLoading ? (
282 const { subscribed, blocked } = this.props.community_view;
286 {subscribed == "NotSubscribed" && (
288 className="btn btn-danger d-block mb-2 w-100"
289 onClick={linkEvent(this, this.handleBlockCommunity)}
291 {i18n.t(blocked ? "unblock_community" : "block_community")}
299 const desc = this.props.community_view.community.description;
302 <div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
308 const community_view = this.props.community_view;
311 <ul className="list-inline mb-1 text-muted font-weight-bold">
312 {amMod(this.props.moderators) && (
314 <li className="list-inline-item-action">
316 className="btn btn-link text-muted d-inline-block"
317 onClick={linkEvent(this, this.handleEditClick)}
318 data-tippy-content={i18n.t("edit")}
319 aria-label={i18n.t("edit")}
321 <Icon icon="edit" classes="icon-inline" />
324 {!amTopMod(this.props.moderators) &&
325 (!this.state.showConfirmLeaveModTeam ? (
326 <li className="list-inline-item-action">
328 className="btn btn-link text-muted d-inline-block"
331 this.handleShowConfirmLeaveModTeamClick
334 {i18n.t("leave_mod_team")}
339 <li className="list-inline-item-action">
340 {i18n.t("are_you_sure")}
342 <li className="list-inline-item-action">
344 className="btn btn-link text-muted d-inline-block"
345 onClick={linkEvent(this, this.handleLeaveModTeam)}
350 <li className="list-inline-item-action">
352 className="btn btn-link text-muted d-inline-block"
355 this.handleCancelLeaveModTeamClick
363 {amTopMod(this.props.moderators) && (
364 <li className="list-inline-item-action">
366 className="btn btn-link text-muted d-inline-block"
367 onClick={linkEvent(this, this.handleDeleteCommunity)}
369 !community_view.community.deleted
374 !community_view.community.deleted
379 {this.state.deleteCommunityLoading ? (
384 classes={`icon-inline ${
385 community_view.community.deleted && "text-danger"
395 <li className="list-inline-item">
396 {!this.props.community_view.community.removed ? (
398 className="btn btn-link text-muted d-inline-block"
399 onClick={linkEvent(this, this.handleModRemoveShow)}
405 className="btn btn-link text-muted d-inline-block"
406 onClick={linkEvent(this, this.handleRemoveCommunity)}
408 {this.state.removeCommunityLoading ? (
416 className="btn btn-link text-muted d-inline-block"
417 onClick={linkEvent(this, this.handlePurgeCommunityShow)}
418 aria-label={i18n.t("purge_community")}
420 {i18n.t("purge_community")}
425 {this.state.showRemoveDialog && (
426 <form onSubmit={linkEvent(this, this.handleRemoveCommunity)}>
427 <div className="input-group mb-3">
428 <label className="col-form-label" htmlFor="remove-reason">
434 className="form-control me-2"
435 placeholder={i18n.t("optional")}
436 value={this.state.removeReason}
437 onInput={linkEvent(this, this.handleModRemoveReasonChange)}
440 {/* TODO hold off on expires for now */}
441 {/* <div class="mb-3 row"> */}
442 {/* <label class="col-form-label">Expires</label> */}
443 {/* <input type="date" class="form-control me-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
445 <div className="input-group mb-3">
446 <button type="submit" className="btn btn-secondary">
447 {this.state.removeCommunityLoading ? (
450 i18n.t("remove_community")
456 {this.state.showPurgeDialog && (
457 <form onSubmit={linkEvent(this, this.handlePurgeCommunity)}>
458 <div className="input-group mb-3">
461 <div className="input-group mb-3">
462 <label className="visually-hidden" htmlFor="purge-reason">
468 className="form-control me-2"
469 placeholder={i18n.t("reason")}
470 value={this.state.purgeReason}
471 onInput={linkEvent(this, this.handlePurgeReasonChange)}
474 <div className="input-group mb-3">
475 {this.state.purgeCommunityLoading ? (
480 className="btn btn-secondary"
481 aria-label={i18n.t("purge_community")}
483 {i18n.t("purge_community")}
493 handleEditClick(i: Sidebar) {
494 i.setState({ showEdit: true });
498 this.setState({ showEdit: false });
501 handleShowConfirmLeaveModTeamClick(i: Sidebar) {
502 i.setState({ showConfirmLeaveModTeam: true });
505 handleCancelLeaveModTeamClick(i: Sidebar) {
506 i.setState({ showConfirmLeaveModTeam: false });
509 get canPost(): boolean {
511 !this.props.community_view.community.posting_restricted_to_mods ||
512 amMod(this.props.moderators) ||
517 handleModRemoveShow(i: Sidebar) {
518 i.setState({ showRemoveDialog: true });
521 handleModRemoveReasonChange(i: Sidebar, event: any) {
522 i.setState({ removeReason: event.target.value });
525 handleModRemoveExpiresChange(i: Sidebar, event: any) {
526 i.setState({ removeExpires: event.target.value });
529 handlePurgeCommunityShow(i: Sidebar) {
530 i.setState({ showPurgeDialog: true, showRemoveDialog: false });
533 handlePurgeReasonChange(i: Sidebar, event: any) {
534 i.setState({ purgeReason: event.target.value });
537 // TODO Do we need two of these?
538 handleUnfollowCommunity(i: Sidebar) {
539 i.setState({ followCommunityLoading: true });
540 i.props.onFollowCommunity({
541 community_id: i.props.community_view.community.id,
543 auth: myAuthRequired(),
547 handleFollowCommunity(i: Sidebar) {
548 i.setState({ followCommunityLoading: true });
549 i.props.onFollowCommunity({
550 community_id: i.props.community_view.community.id,
552 auth: myAuthRequired(),
556 handleBlockCommunity(i: Sidebar) {
557 const { community, blocked } = i.props.community_view;
559 i.props.onBlockCommunity({
560 community_id: community.id,
562 auth: myAuthRequired(),
566 handleLeaveModTeam(i: Sidebar) {
567 const myId = UserService.Instance.myUserInfo?.local_user_view.person.id;
569 i.setState({ leaveModTeamLoading: true });
570 i.props.onLeaveModTeam({
571 community_id: i.props.community_view.community.id,
574 auth: myAuthRequired(),
579 handleDeleteCommunity(i: Sidebar) {
580 i.setState({ deleteCommunityLoading: true });
581 i.props.onDeleteCommunity({
582 community_id: i.props.community_view.community.id,
583 deleted: !i.props.community_view.community.deleted,
584 auth: myAuthRequired(),
588 handleRemoveCommunity(i: Sidebar, event: any) {
589 event.preventDefault();
590 i.setState({ removeCommunityLoading: true });
591 i.props.onRemoveCommunity({
592 community_id: i.props.community_view.community.id,
593 removed: !i.props.community_view.community.removed,
594 reason: i.state.removeReason,
595 expires: getUnixTime(i.state.removeExpires), // TODO fix this
596 auth: myAuthRequired(),
600 handlePurgeCommunity(i: Sidebar, event: any) {
601 event.preventDefault();
602 i.setState({ purgeCommunityLoading: true });
603 i.props.onPurgeCommunity({
604 community_id: i.props.community_view.community.id,
605 reason: i.state.purgeReason,
606 auth: myAuthRequired(),