diff options
author | Dessalines <dessalines@users.noreply.github.com> | 2020-07-09 20:03:47 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-09 20:03:47 -0400 |
commit | 50e6d81d0b40e1d9caa0db83d20026adf3aef631 (patch) | |
tree | 78d3c0770ec4508dbc6c565372e93ddf8c577634 | |
parent | 85c07e7154c82e5b387bb6a02aae70645cf1d8e0 (diff) |
Redirect to login page for votes, comments, pages, etc. Fixes #849 (#926)
-rw-r--r-- | ui/src/components/comment-form.tsx | 255 | ||||
-rw-r--r-- | ui/src/components/create-community.tsx | 7 | ||||
-rw-r--r-- | ui/src/components/create-post.tsx | 7 | ||||
-rw-r--r-- | ui/src/components/create-private-message.tsx | 7 | ||||
-rw-r--r-- | ui/src/components/post-listing.tsx | 9 | ||||
-rw-r--r-- | ui/translations/en.json | 1 |
6 files changed, 164 insertions, 122 deletions
diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index 32bc3786..a433dbd4 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -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> ); } diff --git a/ui/src/components/create-community.tsx b/ui/src/components/create-community.tsx index 86929894..3a5d943d 100644 --- a/ui/src/components/create-community.tsx +++ b/ui/src/components/create-community.tsx @@ -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( diff --git a/ui/src/components/create-post.tsx b/ui/src/components/create-post.tsx index 348ba0cb..4554326d 100644 --- a/ui/src/components/create-post.tsx +++ b/ui/src/components/create-post.tsx @@ -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( diff --git a/ui/src/components/create-private-message.tsx b/ui/src/components/create-private-message.tsx index 21ed04c7..c309cbe3 100644 --- a/ui/src/components/create-private-message.tsx +++ b/ui/src/components/create-private-message.tsx @@ -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( diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index fa2a078e..418fe7b4 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -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) { diff --git a/ui/translations/en.json b/ui/translations/en.json index 56e6e28a..89f69f69 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -220,6 +220,7 @@ "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", |