]> Untitled Git - lemmy.git/commitdiff
Redirect to login page for votes, comments, pages, etc. Fixes #849 (#926)
authorDessalines <dessalines@users.noreply.github.com>
Fri, 10 Jul 2020 00:03:47 +0000 (20:03 -0400)
committerGitHub <noreply@github.com>
Fri, 10 Jul 2020 00:03:47 +0000 (20:03 -0400)
ui/src/components/comment-form.tsx
ui/src/components/create-community.tsx
ui/src/components/create-post.tsx
ui/src/components/create-private-message.tsx
ui/src/components/post-listing.tsx
ui/translations/en.json

index 32bc37860b6b428c1990c417fb974ad65644eef2..a433dbd4268d9bd99720e13b8846777193ecead4 100644 (file)
@@ -1,4 +1,5 @@
 import { Component, linkEvent } from 'inferno';
+import { Link } from 'inferno-router';
 import { Subscription } from 'rxjs';
 import { retryWhen, delay, take } from 'rxjs/operators';
 import { Prompt } from 'inferno-router';
@@ -25,6 +26,7 @@ import autosize from 'autosize';
 import Tribute from 'tributejs/src/Tribute.js';
 import emojiShortName from 'emoji-short-name';
 import { i18n } from '../i18next';
+import { T } from 'inferno-i18next';
 
 interface CommentFormProps {
   postId?: number;
@@ -99,29 +101,31 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
 
   componentDidMount() {
     let textarea: any = document.getElementById(this.id);
-    autosize(textarea);
-    this.tribute.attach(textarea);
-    textarea.addEventListener('tribute-replaced', () => {
-      this.state.commentForm.content = textarea.value;
-      this.setState(this.state);
-      autosize.update(textarea);
-    });
+    if (textarea) {
+      autosize(textarea);
+      this.tribute.attach(textarea);
+      textarea.addEventListener('tribute-replaced', () => {
+        this.state.commentForm.content = textarea.value;
+        this.setState(this.state);
+        autosize.update(textarea);
+      });
 
-    // Quoting of selected text
-    let selectedText = window.getSelection().toString();
-    if (selectedText) {
-      let quotedText =
-        selectedText
-          .split('\n')
-          .map(t => `> ${t}`)
-          .join('\n') + '\n\n';
-      this.state.commentForm.content = quotedText;
-      this.setState(this.state);
-      // Not sure why this needs a delay
-      setTimeout(() => autosize.update(textarea), 10);
-    }
+      // Quoting of selected text
+      let selectedText = window.getSelection().toString();
+      if (selectedText) {
+        let quotedText =
+          selectedText
+            .split('\n')
+            .map(t => `> ${t}`)
+            .join('\n') + '\n\n';
+        this.state.commentForm.content = quotedText;
+        this.setState(this.state);
+        // Not sure why this needs a delay
+        setTimeout(() => autosize.update(textarea), 10);
+      }
 
-    textarea.focus();
+      textarea.focus();
+    }
   }
 
   componentDidUpdate() {
@@ -144,115 +148,128 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
           when={this.state.commentForm.content}
           message={i18n.t('block_leaving')}
         />
-        <form
-          id={this.formId}
-          onSubmit={linkEvent(this, this.handleCommentSubmit)}
-        >
-          <div class="form-group row">
-            <div className={`col-sm-12`}>
-              <textarea
-                id={this.id}
-                className={`form-control ${this.state.previewMode && 'd-none'}`}
-                value={this.state.commentForm.content}
-                onInput={linkEvent(this, this.handleCommentContentChange)}
-                onPaste={linkEvent(this, this.handleImageUploadPaste)}
-                required
-                disabled={this.props.disabled}
-                rows={2}
-                maxLength={10000}
-              />
-              {this.state.previewMode && (
-                <div
-                  className="card card-body md-div"
-                  dangerouslySetInnerHTML={mdToHtml(
-                    this.state.commentForm.content
-                  )}
+        {UserService.Instance.user ? (
+          <form
+            id={this.formId}
+            onSubmit={linkEvent(this, this.handleCommentSubmit)}
+          >
+            <div class="form-group row">
+              <div className={`col-sm-12`}>
+                <textarea
+                  id={this.id}
+                  className={`form-control ${
+                    this.state.previewMode && 'd-none'
+                  }`}
+                  value={this.state.commentForm.content}
+                  onInput={linkEvent(this, this.handleCommentContentChange)}
+                  onPaste={linkEvent(this, this.handleImageUploadPaste)}
+                  required
+                  disabled={this.props.disabled}
+                  rows={2}
+                  maxLength={10000}
                 />
-              )}
-            </div>
-          </div>
-          <div class="row">
-            <div class="col-sm-12">
-              <button
-                type="submit"
-                class="btn btn-sm btn-secondary mr-2"
-                disabled={this.props.disabled || this.state.loading}
-              >
-                {this.state.loading ? (
-                  <svg class="icon icon-spinner spin">
-                    <use xlinkHref="#icon-spinner"></use>
-                  </svg>
-                ) : (
-                  <span>{this.state.buttonTitle}</span>
+                {this.state.previewMode && (
+                  <div
+                    className="card card-body md-div"
+                    dangerouslySetInnerHTML={mdToHtml(
+                      this.state.commentForm.content
+                    )}
+                  />
                 )}
-              </button>
-              {this.state.commentForm.content && (
-                <button
-                  className={`btn btn-sm mr-2 btn-secondary ${
-                    this.state.previewMode && 'active'
-                  }`}
-                  onClick={linkEvent(this, this.handlePreviewToggle)}
-                >
-                  {i18n.t('preview')}
-                </button>
-              )}
-              {this.props.node && (
+              </div>
+            </div>
+            <div class="row">
+              <div class="col-sm-12">
                 <button
-                  type="button"
+                  type="submit"
                   class="btn btn-sm btn-secondary mr-2"
-                  onClick={linkEvent(this, this.handleReplyCancel)}
+                  disabled={this.props.disabled || this.state.loading}
                 >
-                  {i18n.t('cancel')}
+                  {this.state.loading ? (
+                    <svg class="icon icon-spinner spin">
+                      <use xlinkHref="#icon-spinner"></use>
+                    </svg>
+                  ) : (
+                    <span>{this.state.buttonTitle}</span>
+                  )}
                 </button>
-              )}
-              <a
-                href={markdownHelpUrl}
-                target="_blank"
-                class="d-inline-block float-right text-muted font-weight-bold"
-                title={i18n.t('formatting_help')}
-                rel="noopener"
-              >
-                <svg class="icon icon-inline">
-                  <use xlinkHref="#icon-help-circle"></use>
-                </svg>
-              </a>
-              <form class="d-inline-block mr-3 float-right text-muted font-weight-bold">
-                <label
-                  htmlFor={`file-upload-${this.id}`}
-                  className={`${UserService.Instance.user && 'pointer'}`}
-                  data-tippy-content={i18n.t('upload_image')}
+                {this.state.commentForm.content && (
+                  <button
+                    className={`btn btn-sm mr-2 btn-secondary ${
+                      this.state.previewMode && 'active'
+                    }`}
+                    onClick={linkEvent(this, this.handlePreviewToggle)}
+                  >
+                    {i18n.t('preview')}
+                  </button>
+                )}
+                {this.props.node && (
+                  <button
+                    type="button"
+                    class="btn btn-sm btn-secondary mr-2"
+                    onClick={linkEvent(this, this.handleReplyCancel)}
+                  >
+                    {i18n.t('cancel')}
+                  </button>
+                )}
+                <a
+                  href={markdownHelpUrl}
+                  target="_blank"
+                  class="d-inline-block float-right text-muted font-weight-bold"
+                  title={i18n.t('formatting_help')}
+                  rel="noopener"
                 >
                   <svg class="icon icon-inline">
-                    <use xlinkHref="#icon-image"></use>
+                    <use xlinkHref="#icon-help-circle"></use>
                   </svg>
-                </label>
-                <input
-                  id={`file-upload-${this.id}`}
-                  type="file"
-                  accept="image/*,video/*"
-                  name="file"
-                  class="d-none"
-                  disabled={!UserService.Instance.user}
-                  onChange={linkEvent(this, this.handleImageUpload)}
-                />
-              </form>
-              {this.state.imageLoading && (
-                <svg class="icon icon-spinner spin">
-                  <use xlinkHref="#icon-spinner"></use>
-                </svg>
-              )}
-              <span
-                onClick={linkEvent(this, this.handleEmojiPickerClick)}
-                class="pointer unselectable d-inline-block mr-3 float-right text-muted font-weight-bold"
-                data-tippy-content={i18n.t('emoji_picker')}
-              >
-                <svg class="icon icon-inline">
-                  <use xlinkHref="#icon-smile"></use>
-                </svg>
-              </span>
+                </a>
+                <form class="d-inline-block mr-3 float-right text-muted font-weight-bold">
+                  <label
+                    htmlFor={`file-upload-${this.id}`}
+                    className={`${UserService.Instance.user && 'pointer'}`}
+                    data-tippy-content={i18n.t('upload_image')}
+                  >
+                    <svg class="icon icon-inline">
+                      <use xlinkHref="#icon-image"></use>
+                    </svg>
+                  </label>
+                  <input
+                    id={`file-upload-${this.id}`}
+                    type="file"
+                    accept="image/*,video/*"
+                    name="file"
+                    class="d-none"
+                    disabled={!UserService.Instance.user}
+                    onChange={linkEvent(this, this.handleImageUpload)}
+                  />
+                </form>
+                {this.state.imageLoading && (
+                  <svg class="icon icon-spinner spin">
+                    <use xlinkHref="#icon-spinner"></use>
+                  </svg>
+                )}
+                <span
+                  onClick={linkEvent(this, this.handleEmojiPickerClick)}
+                  class="pointer unselectable d-inline-block mr-3 float-right text-muted font-weight-bold"
+                  data-tippy-content={i18n.t('emoji_picker')}
+                >
+                  <svg class="icon icon-inline">
+                    <use xlinkHref="#icon-smile"></use>
+                  </svg>
+                </span>
+              </div>
             </div>
+          </form>
+        ) : (
+          <div class="alert alert-warning" role="alert">
+            <svg class="icon icon-inline mr-2">
+              <use xlinkHref="#icon-alert-triangle"></use>
+            </svg>
+            <T i18nKey="must_login" class="d-inline">
+              #<Link to="/login">#</Link>
+            </T>
           </div>
-        </form>
+        )}
       </div>
     );
   }
index 8692989481a933eed9b520d79b028bf39ea4b3ff..3a5d943d4ac64bc81c90056f4958e8860631e103 100644 (file)
@@ -9,7 +9,7 @@ import {
   GetSiteResponse,
 } from '../interfaces';
 import { toast, wsJsonToRes } from '../utils';
-import { WebSocketService } from '../services';
+import { WebSocketService, UserService } from '../services';
 import { i18n } from '../i18next';
 
 interface CreateCommunityState {
@@ -26,6 +26,11 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
     this.handleCommunityCreate = this.handleCommunityCreate.bind(this);
     this.state = this.emptyState;
 
+    if (!UserService.Instance.user) {
+      toast(i18n.t('not_logged_in'), 'danger');
+      this.context.router.history.push(`/login`);
+    }
+
     this.subscription = WebSocketService.Instance.subject
       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
       .subscribe(
index 348ba0cb8a9d171328e928b6d1b0f94a661a91c6..4554326daaa34de2af329a0cf6253d93a035cf49 100644 (file)
@@ -3,7 +3,7 @@ import { Subscription } from 'rxjs';
 import { retryWhen, delay, take } from 'rxjs/operators';
 import { PostForm } from './post-form';
 import { toast, wsJsonToRes } from '../utils';
-import { WebSocketService } from '../services';
+import { WebSocketService, UserService } from '../services';
 import {
   UserOperation,
   PostFormParams,
@@ -41,6 +41,11 @@ export class CreatePost extends Component<any, CreatePostState> {
     this.handlePostCreate = this.handlePostCreate.bind(this);
     this.state = this.emptyState;
 
+    if (!UserService.Instance.user) {
+      toast(i18n.t('not_logged_in'), 'danger');
+      this.context.router.history.push(`/login`);
+    }
+
     this.subscription = WebSocketService.Instance.subject
       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
       .subscribe(
index 21ed04c7814662fc20db94cad648cf7a11151257..c309cbe3e3ce269df06e1146f9ddd3c7e13842a6 100644 (file)
@@ -2,7 +2,7 @@ import { Component } from 'inferno';
 import { Subscription } from 'rxjs';
 import { retryWhen, delay, take } from 'rxjs/operators';
 import { PrivateMessageForm } from './private-message-form';
-import { WebSocketService } from '../services';
+import { WebSocketService, UserService } from '../services';
 import {
   UserOperation,
   WebSocketJsonResponse,
@@ -20,6 +20,11 @@ export class CreatePrivateMessage extends Component<any, any> {
       this
     );
 
+    if (!UserService.Instance.user) {
+      toast(i18n.t('not_logged_in'), 'danger');
+      this.context.router.history.push(`/login`);
+    }
+
     this.subscription = WebSocketService.Instance.subject
       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
       .subscribe(
index fa2a078e4664daf3dc24b743cf197f3a00deffa7..418fe7b486a6c64e301b16f536091a43c23d77ee 100644 (file)
@@ -33,6 +33,7 @@ import {
   setupTippy,
   hostname,
   previewLines,
+  toast,
 } from '../utils';
 import { i18n } from '../i18next';
 
@@ -1032,6 +1033,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handlePostLike(i: PostListing) {
+    if (!UserService.Instance.user) {
+      this.context.router.history.push(`/login`);
+    }
+
     let new_vote = i.state.my_vote == 1 ? 0 : 1;
 
     if (i.state.my_vote == 1) {
@@ -1059,6 +1064,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handlePostDisLike(i: PostListing) {
+    if (!UserService.Instance.user) {
+      this.context.router.history.push(`/login`);
+    }
+
     let new_vote = i.state.my_vote == -1 ? 0 : -1;
 
     if (i.state.my_vote == 1) {
index 56e6e28ad599be63781c00bb9c5409f94f137a0f..89f69f69c492e767039952c0642d8a2f8f03b7f9 100644 (file)
       "Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>. <14></14> <15>Thank you to our contributors: </15> dessalines, Nutomic, asonix, zacanger, and iav.",
     "not_logged_in": "Not logged in.",
     "logged_in": "Logged in.",
+    "must_login": "You must <1>log in or register</1> to comment.",
     "site_saved": "Site Saved.",
     "community_ban": "You have been banned from this community.",
     "site_ban": "You have been banned from the site",