]> 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 0e95666fc9903bec2c9ded2bfb6fdc4ed2d7bcba..25cbd79722b65866309624119b3225ae9ac6bee7 100644 (file)
@@ -4,19 +4,26 @@ import {
   Community,
   CommunityUser,
   FollowCommunityForm,
-  CommunityForm as CommunityFormI,
+  DeleteCommunityForm,
+  RemoveCommunityForm,
   UserView,
-} from '../interfaces';
+  AddModToCommunityForm,
+} from 'lemmy-js-client';
 import { WebSocketService, UserService } from '../services';
-import { mdToHtml, getUnixTime, pictshareAvatarThumbnail } 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';
-import { T } from 'inferno-i18next';
 
 interface SidebarProps {
   community: Community;
   moderators: Array<CommunityUser>;
   admins: Array<UserView>;
+  online: number;
+  enableNsfw: boolean;
+  showIcon?: boolean;
 }
 
 interface SidebarState {
@@ -24,6 +31,7 @@ interface SidebarState {
   showRemoveDialog: boolean;
   removeReason: string;
   removeExpires: string;
+  showConfirmLeaveModTeam: boolean;
 }
 
 export class Sidebar extends Component<SidebarProps, SidebarState> {
@@ -32,6 +40,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
     showRemoveDialog: false,
     removeReason: null,
     removeExpires: null,
+    showConfirmLeaveModTeam: false,
   };
 
   constructor(props: any, context: any) {
@@ -51,6 +60,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
             community={this.props.community}
             onEdit={this.handleEditCommunity}
             onCancel={this.handleEditCancel}
+            enableNsfw={this.props.enableNsfw}
           />
         )}
       </div>
@@ -58,193 +68,297 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   sidebar() {
-    let community = this.props.community;
     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">
-                  <T i18nKey="removed">#</T>
-                </small>
-              )}
-              {community.deleted && (
-                <small className="ml-2 text-muted font-italic">
-                  <T i18nKey="deleted">#</T>
-                </small>
-              )}
-            </h5>
-            <Link className="text-muted" to={`/c/${community.name}`}>
-              /c/{community.name}
-            </Link>
-            <ul class="list-inline mb-1 text-muted small font-weight-bold">
-              {this.canMod && (
-                <>
-                  <li className="list-inline-item">
+            {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)}
+                      onClick={linkEvent(
+                        this,
+                        this.handleShowConfirmLeaveModTeamClick
+                      )}
                     >
-                      <T i18nKey="edit">#</T>
+                      {i18n.t('leave_mod_team')}
                     </span>
                   </li>
-                  {this.amCreator && (
-                    <li className="list-inline-item">
+                ) : (
+                  <>
+                    <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)}
+                        onClick={linkEvent(this, this.handleLeaveModTeamClick)}
                       >
-                        {!community.deleted
-                          ? i18n.t('delete')
-                          : i18n.t('restore')}
+                        {i18n.t('yes')}
                       </span>
                     </li>
-                  )}
-                </>
-              )}
-              {this.canAdmin && (
-                <li className="list-inline-item">
-                  {!this.props.community.removed ? (
-                    <span
-                      class="pointer"
-                      onClick={linkEvent(this, this.handleModRemoveShow)}
-                    >
-                      <T i18nKey="remove">#</T>
-                    </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'
+                      }`}
                     >
-                      <T i18nKey="restore">#</T>
-                    </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">
-                    <T i18nKey="reason">#</T>
-                  </label>
-                  <input
-                    type="text"
-                    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">
-                    <T i18nKey="remove_community">#</T>
-                  </button>
-                </div>
-              </form>
-            )}
-            <ul class="my-1 list-inline">
-              <li className="list-inline-item">
-                <Link className="badge badge-secondary" to="/communities">
-                  {community.category_name}
-                </Link>
-              </li>
-              <li className="list-inline-item badge badge-secondary">
-                <T
-                  i18nKey="number_of_subscribers"
-                  interpolation={{ count: community.number_of_subscribers }}
-                >
-                  #
-                </T>
-              </li>
-              <li className="list-inline-item badge badge-secondary">
-                <T
-                  i18nKey="number_of_posts"
-                  interpolation={{ count: community.number_of_posts }}
-                >
-                  #
-                </T>
-              </li>
-              <li className="list-inline-item badge badge-secondary">
-                <T
-                  i18nKey="number_of_comments"
-                  interpolation={{ count: community.number_of_comments }}
-                >
-                  #
-                </T>
-              </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)}
                 >
-                  <T i18nKey="modlog">#</T>
-                </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">
-                  <Link class="text-info" to={`/u/${mod.user_name}`}>
-                    {mod.avatar && (
-                      <img
-                        height="32"
-                        width="32"
-                        src={pictshareAvatarThumbnail(mod.avatar)}
-                        class="rounded-circle mr-1"
-                      />
-                    )}
-                    <span>{mod.user_name}</span>
-                  </Link>
-                </li>
-              ))}
-            </ul>
-            <Link
-              class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted ||
-                community.removed) &&
-                'no-click'}`}
-              to={`/create_post?community=${community.name}`}
-            >
-              <T i18nKey="create_a_post">#</T>
-            </Link>
-            <div>
-              {community.subscribed ? (
-                <button
-                  class="btn btn-sm btn-secondary btn-block"
-                  onClick={linkEvent(community.id, this.handleUnsubscribe)}
-                >
-                  <T i18nKey="unsubscribe">#</T>
-                </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)}
                 >
-                  <T i18nKey="subscribe">#</T>
-                </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>
+      </>
     );
   }
 
@@ -265,19 +379,36 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   handleDeleteClick(i: Sidebar) {
     event.preventDefault();
-    let deleteForm: CommunityFormI = {
-      name: i.props.community.name,
-      title: i.props.community.title,
-      category_id: i.props.community.category_id,
+    let deleteForm: DeleteCommunityForm = {
       edit_id: i.props.community.id,
       deleted: !i.props.community.deleted,
-      nsfw: i.props.community.nsfw,
-      auth: null,
     };
-    WebSocketService.Instance.editCommunity(deleteForm);
+    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,
@@ -286,6 +417,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
   }
 
   handleSubscribe(communityId: number) {
+    event.preventDefault();
     let form: FollowCommunityForm = {
       community_id: communityId,
       follow: true,
@@ -331,18 +463,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
 
   handleModRemoveSubmit(i: Sidebar) {
     event.preventDefault();
-    let deleteForm: CommunityFormI = {
-      name: i.props.community.name,
-      title: i.props.community.title,
-      category_id: i.props.community.category_id,
+    let removeForm: RemoveCommunityForm = {
       edit_id: i.props.community.id,
       removed: !i.props.community.removed,
       reason: i.state.removeReason,
       expires: getUnixTime(i.state.removeExpires),
-      nsfw: i.props.community.nsfw,
-      auth: null,
     };
-    WebSocketService.Instance.editCommunity(deleteForm);
+    WebSocketService.Instance.removeCommunity(removeForm);
 
     i.state.showRemoveDialog = false;
     i.setState(i.state);