diff options
author | derek <wwsage@gmail.com> | 2020-07-13 20:09:39 -0400 |
---|---|---|
committer | derek <wwsage@gmail.com> | 2020-07-13 20:09:39 -0400 |
commit | d71897620ceea58b2d314242ea2f646a466d338e (patch) | |
tree | 8b32a1e5f3e06e7c2a6be03827730bb8fdb0679e /ui/src/components | |
parent | e7b9ab1b3a4926d5d32ed9e87792cc9d39d4ede5 (diff) | |
parent | 52983907c4d1b7fda1182316cb631f9b5e913f5b (diff) |
Merge remote-tracking branch 'LemmyNet/master'
Diffstat (limited to 'ui/src/components')
-rw-r--r-- | ui/src/components/cake-day.tsx | 25 | ||||
-rw-r--r-- | ui/src/components/comment-form.tsx | 259 | ||||
-rw-r--r-- | ui/src/components/comment-node.tsx | 32 | ||||
-rw-r--r-- | ui/src/components/communities.tsx | 2 | ||||
-rw-r--r-- | ui/src/components/community.tsx | 2 | ||||
-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/inbox.tsx | 33 | ||||
-rw-r--r-- | ui/src/components/main.tsx | 12 | ||||
-rw-r--r-- | ui/src/components/post-form.tsx | 85 | ||||
-rw-r--r-- | ui/src/components/post-listing.tsx | 11 | ||||
-rw-r--r-- | ui/src/components/post.tsx | 173 | ||||
-rw-r--r-- | ui/src/components/search.tsx | 52 | ||||
-rw-r--r-- | ui/src/components/symbols.tsx | 3 | ||||
-rw-r--r-- | ui/src/components/user-listing.tsx | 35 | ||||
-rw-r--r-- | ui/src/components/user.tsx | 57 |
17 files changed, 495 insertions, 307 deletions
diff --git a/ui/src/components/cake-day.tsx b/ui/src/components/cake-day.tsx new file mode 100644 index 00000000..f28be33c --- /dev/null +++ b/ui/src/components/cake-day.tsx @@ -0,0 +1,25 @@ +import { Component } from 'inferno'; +import { i18n } from '../i18next'; + +interface CakeDayProps { + creatorName: string; +} + +export class CakeDay extends Component<CakeDayProps, any> { + render() { + return ( + <div + className={`mx-2 d-inline-block unselectable pointer`} + data-tippy-content={this.cakeDayTippy()} + > + <svg class="icon icon-inline"> + <use xlinkHref="#icon-cake"></use> + </svg> + </div> + ); + } + + cakeDayTippy(): string { + return i18n.t('cake_day_info', { creator_name: this.props.creatorName }); + } +} diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index 770c127c..00b4fe1e 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'; @@ -17,7 +18,6 @@ import { toast, setupTribute, wsJsonToRes, - emojiPicker, pictrsDeleteToast, } from '../utils'; import { WebSocketService, UserService } from '../services'; @@ -25,6 +25,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; @@ -32,6 +33,7 @@ interface CommentFormProps { onReplyCancel?(): any; edit?: boolean; disabled?: boolean; + focus?: boolean; } interface CommentFormState { @@ -72,7 +74,6 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { super(props, context); this.tribute = setupTribute(); - this.setupEmojiPicker(); this.state = this.emptyState; @@ -98,14 +99,34 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { } componentDidMount() { - var 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); - }); + let textarea: any = document.getElementById(this.id); + 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); + } + + if (this.props.focus) { + textarea.focus(); + } + } } componentDidUpdate() { @@ -128,133 +149,123 @@ 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> + )} + </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> ); } - setupEmojiPicker() { - emojiPicker.on('emoji', twemojiHtmlStr => { - if (this.state.commentForm.content == null) { - this.state.commentForm.content = ''; - } - var el = document.createElement('div'); - el.innerHTML = twemojiHtmlStr; - let nativeUnicode = (el.childNodes[0] as HTMLElement).getAttribute('alt'); - let shortName = `:${emojiShortName[nativeUnicode]}:`; - this.state.commentForm.content += shortName; - this.setState(this.state); - }); - } - handleFinished(op: UserOperation, data: CommentResponse) { let isReply = this.props.node !== undefined && data.comment.parent_id !== null; @@ -302,10 +313,6 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { i.setState(i.state); } - handleEmojiPickerClick(_i: CommentForm, event: any) { - emojiPicker.togglePicker(event.target); - } - handleCommentContentChange(i: CommentForm, event: any) { i.state.commentForm.content = event.target.value; i.setState(i.state); diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 839a01dc..a6b9b7ba 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -32,6 +32,7 @@ import { MomentTime } from './moment-time'; import { CommentForm } from './comment-form'; import { CommentNodes } from './comment-nodes'; import { UserListing } from './user-listing'; +import { CommunityLink } from './community-link'; import { i18n } from '../i18next'; interface CommentNodeState { @@ -158,9 +159,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { id: node.comment.creator_id, local: node.comment.creator_local, actor_id: node.comment.creator_actor_id, + published: node.comment.creator_published, }} /> </span> + {this.isMod && ( <div className="badge badge-light d-none d-sm-inline mr-2"> {i18n.t('mod')} @@ -184,13 +187,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {this.props.showCommunity && ( <> <span class="mx-1">{i18n.t('to')}</span> - <Link class="mr-2" to={`/c/${node.comment.community_name}`}> - {node.comment.community_name} + <CommunityLink + community={{ + name: node.comment.community_name, + id: node.comment.community_id, + local: node.comment.community_local, + actor_id: node.comment.community_actor_id, + }} + /> + <span class="mx-2">•</span> + <Link class="mr-2" to={`/post/${node.comment.post_id}`}> + {node.comment.post_name} </Link> </> )} - <div - className="mr-lg-4 flex-grow-1 flex-lg-grow-0 unselectable pointer mx-2" + <button + class="btn btn-sm text-muted" onClick={linkEvent(this, this.handleCommentCollapse)} > {this.state.collapsed ? ( @@ -202,9 +214,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { <use xlinkHref="#icon-minus-square"></use> </svg> )} - </div> - <span - className={`unselectable pointer ${this.scoreColor}`} + </button> + {/* This is an expanding spacer for mobile */} + <div className="mr-lg-4 flex-grow-1 flex-lg-grow-0 unselectable pointer mx-2"></div> + <button + className={`btn btn-sm p-0 unselectable pointer ${this.scoreColor}`} onClick={linkEvent(node, this.handleCommentUpvote)} data-tippy-content={this.pointsTippy} > @@ -212,7 +226,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { <use xlinkHref="#icon-zap"></use> </svg> <span class="mr-1">{this.state.score}</span> - </span> + </button> <span className="mr-1">•</span> <span> <MomentTime data={node.comment} /> @@ -225,6 +239,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { edit onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} + focus /> )} {!this.state.showEdit && !this.state.collapsed && ( @@ -693,6 +708,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { node={node} onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} + focus /> )} {node.children && !this.state.collapsed && ( diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index 441f7bb1..10a3ab80 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -160,7 +160,7 @@ export class Communities extends Component<any, CommunitiesState> { </button> )} - {this.state.communities.length == communityLimit && ( + {this.state.communities.length > 0 && ( <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)} diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index ff50c3dc..fc999b25 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -260,7 +260,7 @@ export class Community extends Component<any, State> { {i18n.t('prev')} </button> )} - {this.state.posts.length == fetchLimit && ( + {this.state.posts.length > 0 && ( <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)} 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/inbox.tsx b/ui/src/components/inbox.tsx index a88d45c5..8e148921 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -267,6 +267,7 @@ export class Inbox extends Component<any, InboxState> { nodes={[{ comment: i }]} noIndent markable + showCommunity showContext enableDownvotes={this.state.enableDownvotes} /> @@ -285,6 +286,7 @@ export class Inbox extends Component<any, InboxState> { nodes={commentsToFlatNodes(this.state.replies)} noIndent markable + showCommunity showContext enableDownvotes={this.state.enableDownvotes} /> @@ -300,6 +302,7 @@ export class Inbox extends Component<any, InboxState> { nodes={[{ comment: mention }]} noIndent markable + showCommunity showContext enableDownvotes={this.state.enableDownvotes} /> @@ -329,12 +332,14 @@ export class Inbox extends Component<any, InboxState> { {i18n.t('prev')} </button> )} - <button - class="btn btn-sm btn-secondary" - onClick={linkEvent(this, this.nextPage)} - > - {i18n.t('next')} - </button> + {this.unreadCount() > 0 && ( + <button + class="btn btn-sm btn-secondary" + onClick={linkEvent(this, this.nextPage)} + > + {i18n.t('next')} + </button> + )} </div> ); } @@ -534,15 +539,19 @@ export class Inbox extends Component<any, InboxState> { } sendUnreadCount() { - let count = + UserService.Instance.user.unreadCount = this.unreadCount(); + UserService.Instance.sub.next({ + user: UserService.Instance.user, + }); + } + + unreadCount(): number { + return ( this.state.replies.filter(r => !r.read).length + this.state.mentions.filter(r => !r.read).length + this.state.messages.filter( r => !r.read && r.creator_id !== UserService.Instance.user.id - ).length; - UserService.Instance.user.unreadCount = count; - UserService.Instance.sub.next({ - user: UserService.Instance.user, - }); + ).length + ); } } diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 20735049..9063a039 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -373,17 +373,21 @@ export class Main extends Component<any, MainState> { # </a> <a href="https://en.wikipedia.org/wiki/Fediverse">#</a> - <br></br> + <br class="big"></br> <code>#</code> <br></br> <b>#</b> - <br></br> + <br class="big"></br> <a href={repoUrl}>#</a> - <br></br> + <br class="big"></br> <a href="https://www.rust-lang.org">#</a> <a href="https://actix.rs/">#</a> <a href="https://infernojs.org">#</a> <a href="https://www.typescriptlang.org/">#</a> + <br class="big"></br> + <a href="https://github.com/LemmyNet/lemmy/graphs/contributors?type=a"> + # + </a> </T> </p> </div> @@ -493,7 +497,7 @@ export class Main extends Component<any, MainState> { {i18n.t('prev')} </button> )} - {this.state.posts.length == fetchLimit && ( + {this.state.posts.length > 0 && ( <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)} diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx index a88d38c7..e5efeaac 100644 --- a/ui/src/components/post-form.tsx +++ b/ui/src/components/post-form.tsx @@ -33,14 +33,14 @@ import { randomStr, setupTribute, setupTippy, - emojiPicker, hostname, pictrsDeleteToast, + validTitle, } from '../utils'; import autosize from 'autosize'; import Tribute from 'tributejs/src/Tribute.js'; import emojiShortName from 'emoji-short-name'; -import Selectr from 'mobius1-selectr'; +import Choices from 'choices.js'; import { i18n } from '../i18next'; const MAX_POST_TITLE_LENGTH = 200; @@ -70,6 +70,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> { private id = `post-form-${randomStr()}`; private tribute: Tribute; private subscription: Subscription; + private choices: Choices; private emptyState: PostFormState = { postForm: { name: null, @@ -95,7 +96,6 @@ export class PostForm extends Component<PostFormProps, PostFormState> { this.fetchPageTitle = debounce(this.fetchPageTitle).bind(this); this.tribute = setupTribute(); - this.setupEmojiPicker(); this.state = this.emptyState; @@ -166,6 +166,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> { componentWillUnmount() { this.subscription.unsubscribe(); + this.choices && this.choices.destroy(); window.onbeforeunload = null; } @@ -271,12 +272,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> { value={this.state.postForm.name} id="post-title" onInput={linkEvent(this, this.handlePostNameChange)} - class="form-control" + class={`form-control ${ + !validTitle(this.state.postForm.name) && 'is-invalid' + }`} required rows={2} minLength={3} maxLength={MAX_POST_TITLE_LENGTH} /> + {!validTitle(this.state.postForm.name) && ( + <div class="invalid-feedback"> + {i18n.t('invalid_post_title')} + </div> + )} {this.state.suggestedPosts.length > 0 && ( <> <div class="my-1 text-muted small font-weight-bold"> @@ -332,15 +340,6 @@ export class PostForm extends Component<PostFormProps, PostFormState> { <use xlinkHref="#icon-help-circle"></use> </svg> </a> - <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> {!this.props.post && ( @@ -420,20 +419,6 @@ export class PostForm extends Component<PostFormProps, PostFormState> { ); } - setupEmojiPicker() { - emojiPicker.on('emoji', twemojiHtmlStr => { - if (this.state.postForm.body == null) { - this.state.postForm.body = ''; - } - var el = document.createElement('div'); - el.innerHTML = twemojiHtmlStr; - let nativeUnicode = (el.childNodes[0] as HTMLElement).getAttribute('alt'); - let shortName = `:${emojiShortName[nativeUnicode]}:`; - this.state.postForm.body += shortName; - this.setState(this.state); - }); - } - handlePostSubmit(i: PostForm, event: any) { event.preventDefault(); @@ -596,10 +581,6 @@ export class PostForm extends Component<PostFormProps, PostFormState> { }); } - handleEmojiPickerClick(_i: PostForm, event: any) { - emojiPicker.togglePicker(event.target); - } - parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); if (msg.error) { @@ -625,11 +606,45 @@ export class PostForm extends Component<PostFormProps, PostFormState> { // Set up select searching let selectId: any = document.getElementById('post-community'); if (selectId) { - let selector = new Selectr(selectId, { nativeDropdown: false }); - selector.on('selectr.select', option => { - this.state.postForm.community_id = Number(option.value); - this.setState(this.state); + this.choices = new Choices(selectId, { + shouldSort: false, + classNames: { + containerOuter: 'choices', + containerInner: 'choices__inner bg-secondary border-0', + input: 'form-control', + inputCloned: 'choices__input--cloned', + list: 'choices__list', + listItems: 'choices__list--multiple', + listSingle: 'choices__list--single', + listDropdown: 'choices__list--dropdown', + item: 'choices__item bg-secondary', + itemSelectable: 'choices__item--selectable', + itemDisabled: 'choices__item--disabled', + itemChoice: 'choices__item--choice', + placeholder: 'choices__placeholder', + group: 'choices__group', + groupHeading: 'choices__heading', + button: 'choices__button', + activeState: 'is-active', + focusSta |