]> Untitled Git - lemmy.git/blobdiff - ui/src/components/sidebar.tsx
routes.api: fix get_captcha endpoint (#1135)
[lemmy.git] / ui / src / components / sidebar.tsx
index b600628f0fe564c0624abedaaf395a4418006e06..25cbd79722b65866309624119b3225ae9ac6bee7 100644 (file)
@@ -7,12 +7,14 @@ import {
   DeleteCommunityForm,
   RemoveCommunityForm,
   UserView,
-} from '../interfaces';
+  AddModToCommunityForm,
+} from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
-import { mdToHtml, getUnixTime, hostname } from '../utils';
+import { mdToHtml, getUnixTime } from '../utils';
 import { CommunityForm } from './community-form';
 import { UserListing } from './user-listing';
 import { CommunityLink } from './community-link';
+import { BannerIconHeader } from './banner-icon-header';
 import { i18n } from '../i18next';
 
 interface SidebarProps {
@@ -21,6 +23,7 @@ interface SidebarProps {
   admins: Array<UserView>;
   online: number;
   enableNsfw: boolean;
+  showIcon?: boolean;
 }
 
 interface SidebarState {
@@ -28,6 +31,7 @@ interface SidebarState {
   showRemoveDialog: boolean;
   removeReason: string;
   removeExpires: string;
+  showConfirmLeaveModTeam: boolean;
 }
 
 export class Sidebar extends Component<SidebarProps, SidebarState> {
@@ -36,6 +40,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     showRemoveDialog: false,
     removeReason: null,
     removeExpires: null,
+    showConfirmLeaveModTeam: false,
   };
 
   constructor(props: any, context: any) {
@@ -63,208 +68,297 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   sidebar() {
-    let community = this.props.community;
-    let name_: string, link: string;
-
-    if (community.local) {
-      name_ = community.name;
-      link = `/c/${community.name}`;
-    } else {
-      name_ = `${community.name}@${hostname(community.actor_id)}`;
-      link = community.actor_id;
-    }
     return (
       <div>
-        <div class="card border-secondary mb-3">
+        <div class="card bg-transparent border-secondary mb-3">
+          <div class="card-header bg-transparent border-secondary">
+            {this.communityTitle()}
+            {this.adminButtons()}
+          </div>
+          <div class="card-body">{this.subscribes()}</div>
+        </div>
+        <div class="card bg-transparent border-secondary mb-3">
           <div class="card-body">
-            <h5 className="mb-0">
-              <span>{community.title}</span>
-              {community.removed && (
-                <small className="ml-2 text-muted font-italic">
-                  {i18n.t('removed')}
-                </small>
-              )}
-              {community.deleted && (
-                <small className="ml-2 text-muted font-italic">
-                  {i18n.t('deleted')}
-                </small>
-              )}
-            </h5>
-            <CommunityLink community={community} realLink />
-            <ul class="list-inline mb-1 text-muted font-weight-bold">
-              {this.canMod && (
-                <>
+            {this.description()}
+            {this.badges()}
+            {this.mods()}
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+  communityTitle() {
+    let community = this.props.community;
+    return (
+      <div>
+        <h5 className="mb-0">
+          {this.props.showIcon && (
+            <BannerIconHeader icon={community.icon} banner={community.banner} />
+          )}
+          <span>{community.title}</span>
+          {community.removed && (
+            <small className="ml-2 text-muted font-italic">
+              {i18n.t('removed')}
+            </small>
+          )}
+          {community.deleted && (
+            <small className="ml-2 text-muted font-italic">
+              {i18n.t('deleted')}
+            </small>
+          )}
+          {community.nsfw && (
+            <small className="ml-2 text-muted font-italic">
+              {i18n.t('nsfw')}
+            </small>
+          )}
+        </h5>
+        <CommunityLink
+          community={community}
+          realLink
+          useApubName
+          muted
+          hideAvatar
+        />
+      </div>
+    );
+  }
+
+  badges() {
+    let community = this.props.community;
+    return (
+      <ul class="my-1 list-inline">
+        <li className="list-inline-item badge badge-light">
+          {i18n.t('number_online', { count: this.props.online })}
+        </li>
+        <li className="list-inline-item badge badge-light">
+          {i18n.t('number_of_subscribers', {
+            count: community.number_of_subscribers,
+          })}
+        </li>
+        <li className="list-inline-item badge badge-light">
+          {i18n.t('number_of_posts', {
+            count: community.number_of_posts,
+          })}
+        </li>
+        <li className="list-inline-item badge badge-light">
+          {i18n.t('number_of_comments', {
+            count: community.number_of_comments,
+          })}
+        </li>
+        <li className="list-inline-item">
+          <Link className="badge badge-light" to="/communities">
+            {community.category_name}
+          </Link>
+        </li>
+        <li className="list-inline-item">
+          <Link
+            className="badge badge-light"
+            to={`/modlog/community/${this.props.community.id}`}
+          >
+            {i18n.t('modlog')}
+          </Link>
+        </li>
+        <li className="list-inline-item badge badge-light">
+          <CommunityLink community={community} realLink />
+        </li>
+      </ul>
+    );
+  }
+
+  mods() {
+    return (
+      <ul class="list-inline small">
+        <li class="list-inline-item">{i18n.t('mods')}: </li>
+        {this.props.moderators.map(mod => (
+          <li class="list-inline-item">
+            <UserListing
+              user={{
+                name: mod.user_name,
+                preferred_username: mod.user_preferred_username,
+                avatar: mod.avatar,
+                id: mod.user_id,
+                local: mod.user_local,
+                actor_id: mod.user_actor_id,
+              }}
+            />
+          </li>
+        ))}
+      </ul>
+    );
+  }
+
+  subscribes() {
+    let community = this.props.community;
+    return (
+      <div class="d-flex flex-wrap">
+        <Link
+          class={`btn btn-secondary flex-fill mr-2 mb-2 ${
+            community.deleted || community.removed ? 'no-click' : ''
+          }`}
+          to={`/create_post?community=${community.name}`}
+        >
+          {i18n.t('create_a_post')}
+        </Link>
+        {community.subscribed ? (
+          <a
+            class="btn btn-secondary flex-fill mb-2"
+            href="#"
+            onClick={linkEvent(community.id, this.handleUnsubscribe)}
+          >
+            {i18n.t('unsubscribe')}
+          </a>
+        ) : (
+          <a
+            class="btn btn-secondary flex-fill mb-2"
+            href="#"
+            onClick={linkEvent(community.id, this.handleSubscribe)}
+          >
+            {i18n.t('subscribe')}
+          </a>
+        )}
+      </div>
+    );
+  }
+
+  description() {
+    let community = this.props.community;
+    return (
+      community.description && (
+        <div
+          className="md-div"
+          dangerouslySetInnerHTML={mdToHtml(community.description)}
+        />
+      )
+    );
+  }
+
+  adminButtons() {
+    let community = this.props.community;
+    return (
+      <>
+        <ul class="list-inline mb-1 text-muted font-weight-bold">
+          {this.canMod && (
+            <>
+              <li className="list-inline-item-action">
+                <span
+                  class="pointer"
+                  onClick={linkEvent(this, this.handleEditClick)}
+                  data-tippy-content={i18n.t('edit')}
+                >
+                  <svg class="icon icon-inline">
+                    <use xlinkHref="#icon-edit"></use>
+                  </svg>
+                </span>
+              </li>
+              {!this.amCreator &&
+                (!this.state.showConfirmLeaveModTeam ? (
                   <li className="list-inline-item-action">
                     <span
                       class="pointer"
-                      onClick={linkEvent(this, this.handleEditClick)}
-                      data-tippy-content={i18n.t('edit')}
+                      onClick={linkEvent(
+                        this,
+                        this.handleShowConfirmLeaveModTeamClick
+                      )}
                     >
-                      <svg class="icon icon-inline">
-                        <use xlinkHref="#icon-edit"></use>
-                      </svg>
+                      {i18n.t('leave_mod_team')}
                     </span>
                   </li>
-                  {this.amCreator && (
+                ) : (
+                  <>
+                    <li className="list-inline-item-action">
+                      {i18n.t('are_you_sure')}
+                    </li>
                     <li className="list-inline-item-action">
                       <span
                         class="pointer"
-                        onClick={linkEvent(this, this.handleDeleteClick)}
-                        data-tippy-content={
-                          !community.deleted
-                            ? i18n.t('delete')
-                            : i18n.t('restore')
-                        }
+                        onClick={linkEvent(this, this.handleLeaveModTeamClick)}
                       >
-                        <svg
-                          class={`icon icon-inline ${
-                            community.deleted && 'text-danger'
-                          }`}
-                        >
-                          <use xlinkHref="#icon-trash"></use>
-                        </svg>
+                        {i18n.t('yes')}
                       </span>
                     </li>
-                  )}
-                </>
-              )}
-              {this.canAdmin && (
-                <li className="list-inline-item">
-                  {!this.props.community.removed ? (
-                    <span
-                      class="pointer"
-                      onClick={linkEvent(this, this.handleModRemoveShow)}
-                    >
-                      {i18n.t('remove')}
-                    </span>
-                  ) : (
-                    <span
-                      class="pointer"
-                      onClick={linkEvent(this, this.handleModRemoveSubmit)}
+                    <li className="list-inline-item-action">
+                      <span
+                        class="pointer"
+                        onClick={linkEvent(
+                          this,
+                          this.handleCancelLeaveModTeamClick
+                        )}
+                      >
+                        {i18n.t('no')}
+                      </span>
+                    </li>
+                  </>
+                ))}
+              {this.amCreator && (
+                <li className="list-inline-item-action">
+                  <span
+                    class="pointer"
+                    onClick={linkEvent(this, this.handleDeleteClick)}
+                    data-tippy-content={
+                      !community.deleted ? i18n.t('delete') : i18n.t('restore')
+                    }
+                  >
+                    <svg
+                      class={`icon icon-inline ${
+                        community.deleted && 'text-danger'
+                      }`}
                     >
-                      {i18n.t('restore')}
-                    </span>
-                  )}
+                      <use xlinkHref="#icon-trash"></use>
+                    </svg>
+                  </span>
                 </li>
               )}
-            </ul>
-            {this.state.showRemoveDialog && (
-              <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
-                <div class="form-group row">
-                  <label class="col-form-label" htmlFor="remove-reason">
-                    {i18n.t('reason')}
-                  </label>
-                  <input
-                    type="text"
-                    id="remove-reason"
-                    class="form-control mr-2"
-                    placeholder={i18n.t('optional')}
-                    value={this.state.removeReason}
-                    onInput={linkEvent(this, this.handleModRemoveReasonChange)}
-                  />
-                </div>
-                {/* TODO hold off on expires for now */}
-                {/* <div class="form-group row"> */}
-                {/*   <label class="col-form-label">Expires</label> */}
-                {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
-                {/* </div> */}
-                <div class="form-group row">
-                  <button type="submit" class="btn btn-secondary">
-                    {i18n.t('remove_community')}
-                  </button>
-                </div>
-              </form>
-            )}
-            <ul class="my-1 list-inline">
-              {/*
-              <li className="list-inline-item badge badge-secondary">
-                {i18n.t('number_online', { count: this.props.online })}
-              </li>
-              */}
-              <li className="list-inline-item badge badge-secondary">
-                {i18n.t('number_of_subscribers', {
-                  count: community.number_of_subscribers,
-                })}
-              </li>
-              <li className="list-inline-item badge badge-secondary">
-                {i18n.t('number_of_posts', {
-                  count: community.number_of_posts,
-                })}
-              </li>
-              <li className="list-inline-item badge badge-secondary">
-                {i18n.t('number_of_comments', {
-                  count: community.number_of_comments,
-                })}
-              </li>
-              <li className="list-inline-item">
-                <Link className="badge badge-secondary" to="/communities">
-                  {community.category_name}
-                </Link>
-              </li>
-              <li className="list-inline-item">
-                <Link
-                  className="badge badge-secondary"
-                  to={`/modlog/community/${this.props.community.id}`}
+            </>
+          )}
+          {this.canAdmin && (
+            <li className="list-inline-item">
+              {!this.props.community.removed ? (
+                <span
+                  class="pointer"
+                  onClick={linkEvent(this, this.handleModRemoveShow)}
                 >
-                  {i18n.t('modlog')}
-                </Link>
-              </li>
-            </ul>
-            <ul class="list-inline small">
-              <li class="list-inline-item">{i18n.t('mods')}: </li>
-              {this.props.moderators.map(mod => (
-                <li class="list-inline-item">
-                  <UserListing
-                    user={{
-                      name: mod.user_name,
-                      avatar: mod.avatar,
-                      id: mod.user_id,
-                      local: mod.user_local,
-                      actor_id: mod.user_actor_id,
-                    }}
-                  />
-                </li>
-              ))}
-            </ul>
-            {/* TODO the to= needs to be able to handle community_ids as well, since they're federated */}
-            <Link
-              class={`btn btn-sm btn-secondary btn-block mb-3 ${
-                (community.deleted || community.removed) && 'no-click'
-              }`}
-              to={`/create_post?community=${community.name}`}
-            >
-              {i18n.t('create_a_post')}
-            </Link>
-            <div>
-              {community.subscribed ? (
-                <button
-                  class="btn btn-sm btn-secondary btn-block"
-                  onClick={linkEvent(community.id, this.handleUnsubscribe)}
-                >
-                  {i18n.t('unsubscribe')}
-                </button>
+                  {i18n.t('remove')}
+                </span>
               ) : (
-                <button
-                  class="btn btn-sm btn-secondary btn-block"
-                  onClick={linkEvent(community.id, this.handleSubscribe)}
+                <span
+                  class="pointer"
+                  onClick={linkEvent(this, this.handleModRemoveSubmit)}
                 >
-                  {i18n.t('subscribe')}
-                </button>
+                  {i18n.t('restore')}
+                </span>
               )}
-            </div>
-          </div>
-        </div>
-        {community.description && (
-          <div class="card border-secondary">
-            <div class="card-body">
-              <div
-                className="md-div"
-                dangerouslySetInnerHTML={mdToHtml(community.description)}
+            </li>
+          )}
+        </ul>
+        {this.state.showRemoveDialog && (
+          <form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
+            <div class="form-group row">
+              <label class="col-form-label" htmlFor="remove-reason">
+                {i18n.t('reason')}
+              </label>
+              <input
+                type="text"
+                id="remove-reason"
+                class="form-control mr-2"
+                placeholder={i18n.t('optional')}
+                value={this.state.removeReason}
+                onInput={linkEvent(this, this.handleModRemoveReasonChange)}
               />
             </div>
-          </div>
+            {/* TODO hold off on expires for now */}
+            {/* <div class="form-group row"> */}
+            {/*   <label class="col-form-label">Expires</label> */}
+            {/*   <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
+            {/* </div> */}
+            <div class="form-group row">
+              <button type="submit" class="btn btn-secondary">
+                {i18n.t('remove_community')}
+              </button>
+            </div>
+          </form>
         )}
-      </div>
+      </>
     );
   }
 
@@ -292,7 +386,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     WebSocketService.Instance.deleteCommunity(deleteForm);
   }
 
+  handleShowConfirmLeaveModTeamClick(i: Sidebar) {
+    i.state.showConfirmLeaveModTeam = true;
+    i.setState(i.state);
+  }
+
+  handleLeaveModTeamClick(i: Sidebar) {
+    let form: AddModToCommunityForm = {
+      user_id: UserService.Instance.user.id,
+      community_id: i.props.community.id,
+      added: false,
+    };
+    WebSocketService.Instance.addModToCommunity(form);
+    i.state.showConfirmLeaveModTeam = false;
+    i.setState(i.state);
+  }
+
+  handleCancelLeaveModTeamClick(i: Sidebar) {
+    i.state.showConfirmLeaveModTeam = false;
+    i.setState(i.state);
+  }
+
   handleUnsubscribe(communityId: number) {
+    event.preventDefault();
     let form: FollowCommunityForm = {
       community_id: communityId,
       follow: false,
@@ -301,6 +417,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleSubscribe(communityId: number) {
+    event.preventDefault();
     let form: FollowCommunityForm = {
       community_id: communityId,
       follow: true,