From 44bbc459736ac53b9150903af14c20a497caf9ab Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 02:29:45 -0500 Subject: [PATCH 01/27] A first pass at adding icons, and tippy tooltips. - Adding icons for post-listing, comment-node, and navbar. - Adding html titles. - Updating moment expand to use users locale. --- ui/assets/css/main.css | 26 +++++ ui/assets/css/tippy.css | 1 + ui/package.json | 1 + ui/src/components/comment-form.tsx | 14 ++- ui/src/components/comment-node.tsx | 141 ++++++++++++++++++++-------- ui/src/components/community.tsx | 1 + ui/src/components/iframely-card.tsx | 2 + ui/src/components/inbox.tsx | 1 + ui/src/components/main.tsx | 2 + ui/src/components/moment-time.tsx | 27 +++++- ui/src/components/navbar.tsx | 32 +++++-- ui/src/components/post-form.tsx | 16 +++- ui/src/components/post-listing.tsx | 133 +++++++++++++++++++------- ui/src/components/symbols.tsx | 57 +++++++---- ui/src/components/user.tsx | 1 + ui/src/index.html | 1 + ui/src/utils.ts | 12 +++ ui/translations/en.json | 2 + ui/yarn.lock | 12 +++ 19 files changed, 373 insertions(+), 109 deletions(-) create mode 100644 ui/assets/css/tippy.css diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index b03f2703..d206a508 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -95,8 +95,17 @@ fill: currentColor; vertical-align: middle; align-self: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } +.icon-inline { + margin-bottom: 2px; +} .spin { animation: spins 2s linear infinite; @@ -225,3 +234,20 @@ hr { height: 50px; width: 50px; } + +.unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.list-inline-item-action { + display: inline-block; +} + +.list-inline-item-action:not(:last-child) { + margin-right: 1.2rem; +} diff --git a/ui/assets/css/tippy.css b/ui/assets/css/tippy.css new file mode 100644 index 00000000..ff0a3132 --- /dev/null +++ b/ui/assets/css/tippy.css @@ -0,0 +1 @@ +.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}.tippy-iOS{cursor:pointer!important;-webkit-tap-highlight-color:transparent}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-width:8px 8px 0;border-top-color:#333;bottom:-7px;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;border-width:0 8px 8px;border-bottom-color:#333;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:#333;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:#333;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index f12f947a..8658d136 100644 --- a/ui/package.json +++ b/ui/package.json @@ -41,6 +41,7 @@ "reconnecting-websocket": "^4.4.0", "rxjs": "^6.4.0", "terser": "^4.6.3", + "tippy.js": "^6.0.0", "toastify-js": "^1.6.2", "tributejs": "^4.1.1", "twemoji": "^12.1.2", diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index eaa054d8..aa8e651d 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -141,16 +141,22 @@ export class CommentForm extends Component { - {i18n.t('formatting_help')} + + + -
+ { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } + componentDidUpdate() { + setupTippy(); + } + componentWillReceiveProps(nextProps: CommentNodeProps) { this.state.my_vote = nextProps.node.comment.my_vote; this.state.upvotes = nextProps.node.comment.upvotes; @@ -128,18 +133,22 @@ export class CommentNode extends Component { this.state.my_vote == 1 ? 'text-info' : 'text-muted' }`} onClick={linkEvent(node, this.handleCommentUpvote)} + data-tippy-content={i18n.t('upvote')} > -
{this.state.score}
+
+ {this.state.score} +
{WebSocketService.Instance.site.enable_downvotes && ( @@ -113,12 +119,16 @@ export class Navbar extends Component { > -
    +
      {this.props.post.duplicates && ( <>
    • @@ -470,65 +492,90 @@ export class PostListing extends Component { )}
    -
      +
        {UserService.Instance.user && ( <> {this.props.showBody && ( <> -
      • +
      • - {post.saved ? i18n.t('unsave') : i18n.t('save')} + + +
      • -
      • +
      • - {i18n.t('cross_post')} + + +
      • )} {this.myPost && this.props.showBody && ( <> -
      • +
      • - {i18n.t('edit')} + + +
      • -
      • +
      • - {!post.deleted - ? i18n.t('delete') - : i18n.t('restore')} + + +
      • )} {!this.state.showAdvanced && this.props.showBody ? ( -
      • +
      • - {i18n.t('more')} + + +
      • ) : ( <> {this.props.showBody && post.body && ( -
      • +
      • { )} {this.canModOnSelf && ( <> -
      • +
      • - {post.locked - ? i18n.t('unlock') - : i18n.t('lock')} + + +
      • -
      • +
      • - {post.stickied - ? i18n.t('unsticky') - : i18n.t('sticky')} + + +
      • diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index b5673263..09efcf68 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,40 +15,66 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - message-square - - image - - - + + - external-link - coffee1 - rss - arrow-down - arrow-up - - mail - + + { - search - github - spinner diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index e2df15e1..e97d7b08 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -267,6 +267,7 @@ export class User extends Component { SortType[this.state.sort] }`} target="_blank" + title="RSS" > # diff --git a/ui/src/index.html b/ui/src/index.html index 24bed724..f39773d0 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -14,6 +14,7 @@ + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 66105720..7ef3e303 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -41,6 +41,7 @@ import markdown_it_container from 'markdown-it-container'; import twemoji from 'twemoji'; import emojiShortName from 'emoji-short-name'; import Toastify from 'toastify-js'; +import tippy from 'tippy.js'; export const repoUrl = 'https://github.com/dessalines/lemmy'; export const markdownHelpUrl = '/docs/about_guide.html'; @@ -477,6 +478,17 @@ export function setupTribute(): Tribute { }); } +let tippyInstance = tippy('[data-tippy-content]'); + +export function setupTippy() { + tippyInstance.forEach(e => e.destroy()); + tippyInstance = tippy('[data-tippy-content]', { + delay: [500, 0], + // Display on "long press" + touch: ['hold', 500], + }); +} + function userSearch(text: string, cb: any) { if (text) { let form: SearchForm = { diff --git a/ui/translations/en.json b/ui/translations/en.json index 8b719c71..1f959396 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -146,6 +146,8 @@ "browser_default": "Browser Default", "downvotes_disabled": "Downvotes disabled", "enable_downvotes": "Enable Downvotes", + "upvote": "Upvote", + "downvote": "Downvote", "open_registration": "Open Registration", "registration_closed": "Registration closed", "enable_nsfw": "Enable NSFW", diff --git a/ui/yarn.lock b/ui/yarn.lock index 601f3067..6c1f222b 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -104,6 +104,11 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@popperjs/core@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.0.6.tgz#5a39ac118811ca844155b0ad5190b8c24f35e533" + integrity sha512-zj7Gw8QC4jmR92eKUvtrZUEpl2ypRbq+qlE4pwf9n2hnUO9BOAcWUs4/Ht+gNIbFt98xtqhLvccdCfD469MzpQ== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -4432,6 +4437,13 @@ tiny-warning@^1.0.0: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tippy.js@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.0.0.tgz#6bc4f477ea82ef344db51ae34f106a8b25f2d02c" + integrity sha512-2NVc5A8cnO9N/Fk+tTU6KEm6m8ZXT1u3pOY756zZ5BE38rWDl/hVyoWhTfM79HW1nEJSpn/VujqAMMZGHsE9qQ== + dependencies: + "@popperjs/core" "^2.0.6" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" From 7d602c26f84e815da507eff754ce5e772ff0b207 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 09:16:36 -0500 Subject: [PATCH 02/27] Some formatting. --- ui/src/components/post-listing.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 5f3fef09..bc23543a 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -476,7 +476,7 @@ export class PostListing extends Component {
      -
        +
          {this.props.post.duplicates && ( <>
        • From b3b4b7977570edda224003efca5396c10363599d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 10:14:50 -0500 Subject: [PATCH 03/27] More additions to icons. - Adding edit and trash icons for sidebars - Adding pencil edit instead of modified. --- ui/src/components/main.tsx | 9 ++++++--- ui/src/components/moment-time.tsx | 11 ++++++++--- ui/src/components/post-listing.tsx | 8 +++++++- ui/src/components/sidebar.tsx | 30 +++++++++++++++++++++++------- ui/src/components/symbols.tsx | 3 +++ 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 014d82d3..3f7d8fa6 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -268,13 +268,16 @@ export class Main extends Component {
          {`${this.state.siteRes.site.name}`}
          {this.canAdmin && ( -
            -
          • +
              +
            • - {i18n.t('edit')} + + +
            diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index a256f785..76e5fe28 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -1,6 +1,6 @@ import { Component } from 'inferno'; import moment from 'moment'; -import { getMomentLanguage, setupTippy } from '../utils'; +import { getMomentLanguage, setupTippy, capitalizeFirstLetter } from '../utils'; import { i18n } from '../i18next'; interface MomentTimeProps { @@ -28,10 +28,15 @@ export class MomentTime extends Component { if (this.props.data.updated) { return ( - {i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()} + + + + {moment.utc(this.props.data.updated).fromNow()} ); } else { diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index bc23543a..6e285ee9 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -579,8 +579,14 @@ export class PostListing extends Component { - {i18n.t('view_source')} + + +
          • )} diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index 5c8ef1a9..a78589d5 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -13,6 +13,7 @@ import { getUnixTime, pictshareAvatarThumbnail, showAvatars, + setupTippy, } from '../utils'; import { CommunityForm } from './community-form'; import { i18n } from '../i18next'; @@ -46,6 +47,10 @@ export class Sidebar extends Component { this.handleEditCancel = this.handleEditCancel.bind(this); } + componentDidUpdate() { + setupTippy(); + } + render() { return (
            @@ -84,26 +89,37 @@ export class Sidebar extends Component { /c/{community.name} -
              +
                {this.canMod && ( <> -
              • +
              • - {i18n.t('edit')} + + +
              • {this.amCreator && ( -
              • +
              • - {!community.deleted - ? i18n.t('delete') - : i18n.t('restore')} + + +
              • )} diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 09efcf68..108fbab3 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -42,6 +42,9 @@ export class Symbols extends Component { + + + From 48594537c038e7c57e232d8de0daa25c7007d44c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 11:07:07 -0500 Subject: [PATCH 04/27] Change action sizes. Add better tippy updating. --- ui/src/components/comment-node.tsx | 15 ++++++++++++--- ui/src/components/post-listing.tsx | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 3db87aa5..4d95073c 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -103,8 +103,16 @@ export class CommentNode extends Component { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } - componentDidUpdate() { - setupTippy(); + componentDidUpdate(prevProps: CommentNodeProps) { + let prevComment = prevProps.node.comment; + let comment = this.props.node.comment; + if ( + prevComment.saved !== comment.saved || + prevComment.deleted !== comment.deleted || + prevComment.read !== comment.read + ) { + setupTippy(); + } } componentWillReceiveProps(nextProps: CommentNodeProps) { @@ -256,7 +264,7 @@ export class CommentNode extends Component { dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} /> )} -
                  +
                    {this.props.markable && (
                  • { handleShowAdvanced(i: CommentNode) { i.state.showAdvanced = !i.state.showAdvanced; i.setState(i.state); + setupTippy(); } } diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 6e285ee9..97d319d8 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -102,8 +102,17 @@ export class PostListing extends Component { } } - componentDidUpdate() { - setupTippy(); + componentDidUpdate(prevProps: PostListingProps) { + let prevPost = prevProps.post; + let post = this.props.post; + if ( + prevPost.saved !== post.saved || + prevPost.deleted !== post.deleted || + prevPost.locked !== post.locked || + prevPost.stickied !== post.stickied + ) { + setupTippy(); + } } componentWillReceiveProps(nextProps: PostListingProps) { @@ -492,7 +501,7 @@ export class PostListing extends Component { )}
                  -
                    +
                      {UserService.Instance.user && ( <> {this.props.showBody && ( @@ -1305,5 +1314,6 @@ export class PostListing extends Component { handleShowAdvanced(i: PostListing) { i.state.showAdvanced = !i.state.showAdvanced; i.setState(i.state); + setupTippy(); } } From 0aec31328d0c75e08480b15a0e822e6ed4dd86a1 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 11:46:29 -0500 Subject: [PATCH 05/27] Removing icon-inline from message icon. --- ui/src/components/comment-node.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 4d95073c..340ef109 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -321,7 +321,7 @@ export class CommentNode extends Component { to={`/create_private_message?recipient_id=${node.comment.creator_id}`} title={i18n.t('message').toLowerCase()} > - + From a5331bce77d6cb04aa7af10007845a211da57d7c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 16:36:11 -0500 Subject: [PATCH 06/27] Only show tribute menu after a minlength of 3 characters. - Fixes #562 --- ui/package.json | 2 +- ui/src/utils.ts | 3 +++ ui/yarn.lock | 8 ++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ui/package.json b/ui/package.json index 8658d136..e3cabae5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -43,7 +43,7 @@ "terser": "^4.6.3", "tippy.js": "^6.0.0", "toastify-js": "^1.6.2", - "tributejs": "^4.1.1", + "tributejs": "^5.0.0", "twemoji": "^12.1.2", "ws": "^7.0.0" }, diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 7ef3e303..d531a7ca 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -446,6 +446,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 3, }, // Users { @@ -459,6 +460,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 3, }, // Communities @@ -473,6 +475,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, + menuShowMinLength: 3, }, ], }); diff --git a/ui/yarn.lock b/ui/yarn.lock index 6c1f222b..d36bef04 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4506,10 +4506,10 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" -tributejs@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-4.1.1.tgz#f169a4ad12e485241140ec1ab987b460950c974c" - integrity sha512-jc+PcaiNzMjCn2LAQb3i4ic94EsSfLW8Jlk1sK2cb6hLcZFalU9ThcF8rxuKkTUKv1GIvTwN8XseLzCXLxB4lw== +tributejs@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.0.0.tgz#2c5301a79c19d7a72d23e995bf7c9f47c2a34f23" + integrity sha512-aPUpq4+NTXRq1OcdoeiFg9d+wM+J0b7dpL7MNVxqo8JIgChtkx8HnsPVl/uZ4Z1ChTF9UI1ffbvTfJRHqJjjAw== ts-node@^8.6.2: version "8.6.2" From 30165ebd15e560c4c4b00f6c6b7e1947f2652982 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 17:37:27 -0500 Subject: [PATCH 07/27] Updating tributejs --- ui/src/utils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/utils.ts b/ui/src/utils.ts index d531a7ca..0376d3ce 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -34,7 +34,7 @@ import { } from './interfaces'; import { UserService, WebSocketService } from './services'; -import Tribute from 'tributejs/src/Tribute.js'; +import Tribute from 'tributejs'; import markdown_it from 'markdown-it'; import markdownitEmoji from 'markdown-it-emoji/light'; import markdown_it_container from 'markdown-it-container'; @@ -427,7 +427,7 @@ export function toast(text: string, background: string = 'success') { }).showToast(); } -export function setupTribute(): Tribute { +export function setupTribute(): any { return new Tribute({ collection: [ // Emojis @@ -445,7 +445,7 @@ export function setupTribute(): Tribute { }), allowSpaces: false, autocompleteMode: true, - menuItemLimit: mentionDropdownFetchLimit, + // menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, // Users @@ -459,7 +459,7 @@ export function setupTribute(): Tribute { }, allowSpaces: false, autocompleteMode: true, - menuItemLimit: mentionDropdownFetchLimit, + // menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, @@ -474,7 +474,7 @@ export function setupTribute(): Tribute { }, allowSpaces: false, autocompleteMode: true, - menuItemLimit: mentionDropdownFetchLimit, + // menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, ], From 107005c4748845df7f54439731337b78bdefd900 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 3 Mar 2020 19:14:36 -0500 Subject: [PATCH 08/27] Adding icon-plus and minus-square. --- ui/src/components/comment-node.tsx | 10 +++++++++- ui/src/components/post-listing.tsx | 8 ++++++-- ui/src/components/symbols.tsx | 6 ++++++ ui/src/utils.ts | 10 +++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 340ef109..d548e9b2 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -242,7 +242,15 @@ export class CommentNode extends Component { className="unselectable pointer text-monospace" onClick={linkEvent(this, this.handleCommentCollapse)} > - {this.state.collapsed ? '[+]' : '[-]'} + {this.state.collapsed ? ( + + + + ) : ( + + + + )}
          diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 97d319d8..193c2a54 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -353,7 +353,9 @@ export class PostListing extends Component { data-tippy-content={i18n.t('expand_here')} onClick={linkEvent(this, this.handleImageExpandClick)} > - [+] + + + ) : ( @@ -361,7 +363,9 @@ export class PostListing extends Component { class="text-monospace unselectable pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleImageExpandClick)} > - [-] + + +
          { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + + + + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 0376d3ce..d531a7ca 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -34,7 +34,7 @@ import { } from './interfaces'; import { UserService, WebSocketService } from './services'; -import Tribute from 'tributejs'; +import Tribute from 'tributejs/src/Tribute.js'; import markdown_it from 'markdown-it'; import markdownitEmoji from 'markdown-it-emoji/light'; import markdown_it_container from 'markdown-it-container'; @@ -427,7 +427,7 @@ export function toast(text: string, background: string = 'success') { }).showToast(); } -export function setupTribute(): any { +export function setupTribute(): Tribute { return new Tribute({ collection: [ // Emojis @@ -445,7 +445,7 @@ export function setupTribute(): any { }), allowSpaces: false, autocompleteMode: true, - // menuItemLimit: mentionDropdownFetchLimit, + menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, // Users @@ -459,7 +459,7 @@ export function setupTribute(): any { }, allowSpaces: false, autocompleteMode: true, - // menuItemLimit: mentionDropdownFetchLimit, + menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, @@ -474,7 +474,7 @@ export function setupTribute(): any { }, allowSpaces: false, autocompleteMode: true, - // menuItemLimit: mentionDropdownFetchLimit, + menuItemLimit: mentionDropdownFetchLimit, menuShowMinLength: 3, }, ], From 5b2383fa5050d30e5acf8399b78fd651ae0b4342 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 09:30:49 -0500 Subject: [PATCH 09/27] Adding icons to stickied, deleted, and locked. --- ui/src/components/post-listing.tsx | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 193c2a54..d702d78b 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -339,7 +339,7 @@ export class PostListing extends Component { title={this.state.url} > {new URL(this.state.url).hostname} - + @@ -391,18 +391,33 @@ export class PostListing extends Component { )} {post.deleted && ( - - {i18n.t('deleted')} + + + + )} {post.locked && ( - - {i18n.t('locked')} + + + + )} {post.stickied && ( - - {i18n.t('stickied')} + + + + )} {post.nsfw && ( From d8bcfd7d44cd346727589515fdb314e45026c455 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 09:37:19 -0500 Subject: [PATCH 10/27] Changing view source icon. --- ui/src/components/comment-node.tsx | 2 +- ui/src/components/post-listing.tsx | 2 +- ui/src/components/symbols.tsx | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index d548e9b2..ecf5e2aa 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -370,7 +370,7 @@ export class CommentNode extends Component { class={`icon icon-inline ${this.state .viewSource && 'text-success'}`} > - +
        • diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index d702d78b..b6cb3f51 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -613,7 +613,7 @@ export class PostListing extends Component { class={`icon icon-inline ${this.state .viewSource && 'text-success'}`} > - + diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index fcf75e30..0c7169d5 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -42,6 +42,9 @@ export class Symbols extends Component { + + + From 8855b91d0c15bcf9273f81736149e74b8c1a2fe4 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 09:42:50 -0500 Subject: [PATCH 11/27] Fixing select alignment. --- ui/src/components/community.tsx | 15 ++++++++------- ui/src/components/main.tsx | 16 +++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 1c875d04..0be549a5 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -194,13 +194,14 @@ export class Community extends Component { selects() { return ( -
          - - - +
          + + + + { selects() { return (
          - - + + + + { target="_blank" title="RSS" > - + # @@ -460,7 +462,7 @@ export class Main extends Component { target="_blank" title="RSS" > - + # From fb355188487bcd1e4185b8a034f95560cc28946d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 11:46:34 -0500 Subject: [PATCH 12/27] Fixing post sorting by stickied on front end. Fixes #575 --- ui/src/components/post-listings.tsx | 2 +- ui/src/utils.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/src/components/post-listings.tsx b/ui/src/components/post-listings.tsx index d61f624d..5e6acc0c 100644 --- a/ui/src/components/post-listings.tsx +++ b/ui/src/components/post-listings.tsx @@ -53,7 +53,7 @@ export class PostListings extends Component { } if (this.props.sort !== undefined) { - postSort(out, this.props.sort); + postSort(out, this.props.sort, this.props.showCommunity == undefined); } return out; diff --git a/ui/src/utils.ts b/ui/src/utils.ts index d531a7ca..5987070c 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -729,7 +729,11 @@ function convertCommentSortType(sort: SortType): CommentSortType { } } -export function postSort(posts: Array, sort: SortType) { +export function postSort( + posts: Array, + sort: SortType, + communityType: boolean +) { // First, put removed and deleted comments at the bottom, then do your other sorts if ( sort == SortType.TopAll || @@ -740,13 +744,17 @@ export function postSort(posts: Array, sort: SortType) { ) { posts.sort( (a, b) => - +a.removed - +b.removed || +a.deleted - +b.deleted || b.score - a.score + +a.removed - +b.removed || + +a.deleted - +b.deleted || + (communityType && +b.stickied - +a.stickied) || + b.score - a.score ); } else if (sort == SortType.New) { posts.sort( (a, b) => +a.removed - +b.removed || +a.deleted - +b.deleted || + (communityType && +b.stickied - +a.stickied) || b.published.localeCompare(a.published) ); } else if (sort == SortType.Hot) { @@ -754,6 +762,7 @@ export function postSort(posts: Array, sort: SortType) { (a, b) => +a.removed - +b.removed || +a.deleted - +b.deleted || + (communityType && +b.stickied - +a.stickied) || hotRankPost(b) - hotRankPost(a) ); } From 2c2918cc9e5e35c0b108b5daeeb2ccfbdee33e0c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 13:52:11 -0500 Subject: [PATCH 13/27] Moving comment voting to action bar. Adding plurals. --- ui/assets/css/main.css | 4 - ui/src/components/comment-node.tsx | 156 ++++++++++++++++------------- ui/src/components/post-listing.tsx | 7 +- ui/src/components/symbols.tsx | 8 +- ui/src/utils.ts | 6 +- ui/translations/en.json | 21 ++-- 6 files changed, 116 insertions(+), 86 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index d206a508..64f086b3 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -74,10 +74,6 @@ border-top: 2px solid var(--dark); } -.comment-node { - margin-bottom: 10px; -} - .vote-bar { margin-top: -6.5px; } diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index ecf5e2aa..820dd901 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -131,47 +131,13 @@ export class CommentNode extends Component { node.comment.parent_id && !this.props.noIndent ? 'ml-4' : '' }`} > - {!this.state.collapsed && ( -
          - -
          - {this.state.score} -
          - {WebSocketService.Instance.site.enable_downvotes && ( - - )} -
          - )}
          -
            +
            • { {i18n.t('banned')}
            • )} -
            • - - - - - {this.state.upvotes} - -
            • -
            • - - - - - {this.state.downvotes} - -
            • + +
            • + + + + + {this.state.score} + +
            • +
            • + + + + + {this.state.upvotes} + +
            • +
            • + + + + + {this.state.downvotes} + +
            • +
              {this.props.showCommunity && (
            • {i18n.t('to')} @@ -272,7 +253,7 @@ export class CommentNode extends Component { dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} /> )} -
                +
                  {this.props.markable && (
                • { )} {UserService.Instance.user && !this.props.viewOnly && ( <> +
                • + +
                • + {WebSocketService.Instance.site.enable_downvotes && ( +
                • + +
                • + )}
                • {
                • -
                • - - - - - -
                • {!this.myComment && (
                • { title={i18n.t('link')} > - +
                • @@ -360,6 +355,27 @@ export class CommentNode extends Component { ) : ( <> +
                • + + + + + +
                • { -
                  +
                  {this.state.score}
                  {WebSocketService.Instance.site.enable_downvotes && ( diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 0c7169d5..b5d7dcdf 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,12 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + + + + @@ -58,7 +64,7 @@ export class Symbols extends Component { - + diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 5987070c..46b393d9 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -446,7 +446,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, - menuShowMinLength: 3, + menuShowMinLength: 2, }, // Users { @@ -460,7 +460,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, - menuShowMinLength: 3, + menuShowMinLength: 2, }, // Communities @@ -475,7 +475,7 @@ export function setupTribute(): Tribute { allowSpaces: false, autocompleteMode: true, menuItemLimit: mentionDropdownFetchLimit, - menuShowMinLength: 3, + menuShowMinLength: 2, }, ], }); diff --git a/ui/translations/en.json b/ui/translations/en.json index 1f959396..b097b3f1 100644 --- a/ui/translations/en.json +++ b/ui/translations/en.json @@ -4,14 +4,16 @@ "no_posts": "No Posts.", "create_a_post": "Create a post", "create_post": "Create Post", - "number_of_posts": "{{count}} Posts", + "number_of_posts": "{{count}} Post", + "number_of_posts_plural": "{{count}} Posts", "posts": "Posts", "related_posts": "These posts might be related", "cross_posts": "This link has also been posted to:", "cross_post": "cross-post", "cross_posted_to": "cross-posted to: ", "comments": "Comments", - "number_of_comments": "{{count}} Comments", + "number_of_comments": "{{count}} Comment", + "number_of_comments_plural": "{{count}} Comments", "remove_comment": "Remove Comment", "communities": "Communities", "users": "Users", @@ -21,7 +23,8 @@ "subscribed_to_communities": "Subscribed to <1>communities", "trending_communities": "Trending <1>communities", "list_of_communities": "List of communities", - "number_of_communities": "{{count}} Communities", + "number_of_communities": "{{count}} Community", + "number_of_communities_plural": "{{count}} Communities", "community_reqs": "lowercase, underscores, and no spaces.", "create_private_message": "Create Private Message", "send_secure_message": "Send Secure Message", @@ -79,10 +82,14 @@ "creator": "creator", "username": "Username", "email_or_username": "Email or Username", - "number_of_users": "{{count}} Users", - "number_of_subscribers": "{{count}} Subscribers", - "number_of_points": "{{count}} Points", - "number_online": "{{count}} Users Online", + "number_of_users": "{{count}} User", + "number_of_users_plural": "{{count}} Users", + "number_of_subscribers": "{{count}} Subscriber", + "number_of_subscribers_plural": "{{count}} Subscribers", + "number_of_points": "{{count}} Point", + "number_of_points_plural": "{{count}} Points", + "number_online": "{{count}} User Online", + "number_online_plural": "{{count}} Users Online", "name": "Name", "title": "Title", "category": "Category", From 7c274fdd35b30b645b08740987423bd633e3ba9e Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:06:03 -0500 Subject: [PATCH 14/27] Add score color to bar --- ui/src/components/comment-node.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 820dd901..cfca0f3b 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -181,7 +181,7 @@ export class CommentNode extends Component { })} >
                • - + @@ -1144,4 +1144,14 @@ export class CommentNode extends Component { i.setState(i.state); setupTippy(); } + + get scoreColor() { + if (this.state.my_vote == 1) { + return 'text-info'; + } else if (this.state.my_vote == -1) { + return 'text-danger'; + } else { + return 'text-muted'; + } + } } From f601d6cd03a879c1f6e490cb4ecf954cb1d04d22 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:13:08 -0500 Subject: [PATCH 15/27] Switching from heart to zap symbol for points. --- ui/src/components/comment-node.tsx | 2 +- ui/src/components/symbols.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index cfca0f3b..9759a13a 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -183,7 +183,7 @@ export class CommentNode extends Component {
                • - + {this.state.score} diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index b5d7dcdf..d44d0cfd 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,9 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + + From 43f783db42b3b48cacd330ac0b3abb561cef7c54 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:18:13 -0500 Subject: [PATCH 16/27] Removing comment node other score colors for clarity. --- ui/src/components/comment-node.tsx | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 9759a13a..fba96db9 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -182,27 +182,23 @@ export class CommentNode extends Component { >
                • - + {this.state.score}
                • - - - - - {this.state.upvotes} - + + + + {this.state.upvotes}
                • - - - - - {this.state.downvotes} - + + + + {this.state.downvotes}
                • {this.props.showCommunity && ( From 6495cd2f65a279eb0124bcef600194e30de5590c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:24:45 -0500 Subject: [PATCH 17/27] Removing suffix on from now. --- ui/src/components/moment-time.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index 76e5fe28..4aba68d2 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -36,7 +36,7 @@ export class MomentTime extends Component { - {moment.utc(this.props.data.updated).fromNow()} + {moment.utc(this.props.data.updated).fromNow(true)} ); } else { @@ -46,7 +46,7 @@ export class MomentTime extends Component { className="pointer unselectable" data-tippy-content={this.format(str)} > - {moment.utc(str).fromNow()} + {moment.utc(str).fromNow(true)} ); } From d14504763a90d9cc1bf91229ca0dde28a92a6bbc Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 14:25:23 -0500 Subject: [PATCH 18/27] Adding separators for comment node title line. --- ui/src/components/comment-node.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index fba96db9..47930377 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -174,6 +174,7 @@ export class CommentNode extends Component { {i18n.t('banned')} )} +
                • { )} +
                • From c999579c05ded42ea6ec68753d59b288123cc652 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 22:35:55 -0500 Subject: [PATCH 19/27] Better tippy loading. Fixes #577 --- ui/src/components/comment-node.tsx | 12 ------------ ui/src/components/community.tsx | 2 ++ ui/src/components/main.tsx | 2 ++ ui/src/components/moment-time.tsx | 6 +----- ui/src/components/navbar.tsx | 7 +------ ui/src/components/post-listing.tsx | 13 ------------- ui/src/components/post.tsx | 5 +++++ ui/src/components/sidebar.tsx | 5 ----- ui/src/services/WebSocketService.ts | 15 +++++++++++---- 9 files changed, 22 insertions(+), 45 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 47930377..a02a3cf8 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -103,18 +103,6 @@ export class CommentNode extends Component { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } - componentDidUpdate(prevProps: CommentNodeProps) { - let prevComment = prevProps.node.comment; - let comment = this.props.node.comment; - if ( - prevComment.saved !== comment.saved || - prevComment.deleted !== comment.deleted || - prevComment.read !== comment.read - ) { - setupTippy(); - } - } - componentWillReceiveProps(nextProps: CommentNodeProps) { this.state.my_vote = nextProps.node.comment.my_vote; this.state.upvotes = nextProps.node.comment.upvotes; diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 0be549a5..4e8e9d1b 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -43,6 +43,7 @@ import { createPostLikeFindRes, editPostFindRes, commentsToFlatNodes, + setupTippy, } from '../utils'; import { i18n } from '../i18next'; @@ -341,6 +342,7 @@ export class Community extends Component { this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.EditPost) { let data = res.data as PostResponse; editPostFindRes(data, this.state.posts); diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 85fa5d33..ee028151 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -52,6 +52,7 @@ import { editPostFindRes, commentsToFlatNodes, commentSortSortType, + setupTippy, } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -620,6 +621,7 @@ export class Main extends Component { this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.CreatePost) { let data = res.data as PostResponse; diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index 4aba68d2..24ab2d89 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -1,6 +1,6 @@ import { Component } from 'inferno'; import moment from 'moment'; -import { getMomentLanguage, setupTippy, capitalizeFirstLetter } from '../utils'; +import { getMomentLanguage, capitalizeFirstLetter } from '../utils'; import { i18n } from '../i18next'; interface MomentTimeProps { @@ -20,10 +20,6 @@ export class MomentTime extends Component { moment.locale(lang); } - componentDidMount() { - setupTippy(); - } - render() { if (this.props.data.updated) { return ( diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 031c2ecb..ef3f8430 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -26,7 +26,6 @@ import { fetchLimit, isCommentType, toast, - setupTippy, } from '../utils'; import { version } from '../version'; import { i18n } from '../i18next'; @@ -85,10 +84,6 @@ export class Navbar extends Component { WebSocketService.Instance.getSite(); } - componentDidMount() { - setupTippy(); - } - render() { return this.navbar(); } @@ -283,7 +278,7 @@ export class Navbar extends Component { } else if (res.op == UserOperation.GetSite) { let data = res.data as GetSiteResponse; - if (data.site) { + if (data.site && !this.state.siteName) { this.state.siteName = data.site.name; WebSocketService.Instance.site = data.site; this.setState(this.state); diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index e170c711..f8f19448 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -102,19 +102,6 @@ export class PostListing extends Component { } } - componentDidUpdate(prevProps: PostListingProps) { - let prevPost = prevProps.post; - let post = this.props.post; - if ( - prevPost.saved !== post.saved || - prevPost.deleted !== post.deleted || - prevPost.locked !== post.locked || - prevPost.stickied !== post.stickied - ) { - setupTippy(); - } - } - componentWillReceiveProps(nextProps: PostListingProps) { this.state.my_vote = nextProps.post.my_vote; this.state.upvotes = nextProps.post.upvotes; diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index d8f662cf..faee23ed 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -37,6 +37,7 @@ import { createCommentLikeRes, createPostLikeRes, commentsToFlatNodes, + setupTippy, } from '../utils'; import { PostListing } from './post-listing'; import { PostListings } from './post-listings'; @@ -370,6 +371,7 @@ export class Post extends Component { } this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.CreateComment) { let data = res.data as CommentResponse; @@ -386,6 +388,7 @@ export class Post extends Component { let data = res.data as CommentResponse; saveCommentRes(data, this.state.comments); this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.CreateCommentLike) { let data = res.data as CommentResponse; createCommentLikeRes(data, this.state.comments); @@ -398,10 +401,12 @@ export class Post extends Component { let data = res.data as PostResponse; this.state.post = data.post; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.SavePost) { let data = res.data as PostResponse; this.state.post = data.post; this.setState(this.state); + setupTippy(); } else if (res.op == UserOperation.EditCommunity) { let data = res.data as CommunityResponse; this.state.community = data.community; diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index a78589d5..042b67db 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -13,7 +13,6 @@ import { getUnixTime, pictshareAvatarThumbnail, showAvatars, - setupTippy, } from '../utils'; import { CommunityForm } from './community-form'; import { i18n } from '../i18next'; @@ -47,10 +46,6 @@ export class Sidebar extends Component { this.handleEditCancel = this.handleEditCancel.bind(this); } - componentDidUpdate() { - setupTippy(); - } - render() { return (
                  diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index 3df69457..02c97cc9 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -61,6 +61,7 @@ export class WebSocketService { private constructor() { this.ws = new ReconnectingWebSocket(wsUri); + let firstConnect = true; this.subject = Observable.create((obs: any) => { this.ws.onmessage = e => { @@ -68,13 +69,19 @@ export class WebSocketService { }; this.ws.onopen = () => { console.log(`Connected to ${wsUri}`); + if (UserService.Instance.user) { this.userJoin(); } - let res: WebSocketJsonResponse = { - reconnect: true, - }; - obs.next(res); + + if (!firstConnect) { + let res: WebSocketJsonResponse = { + reconnect: true, + }; + obs.next(res); + } + + firstConnect = false; }; }).pipe(share()); } From eeef752a5ce1af441c69a701d95ba8a2dec651c4 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 4 Mar 2020 23:36:42 -0500 Subject: [PATCH 20/27] Adding left border color, removing indent. --- ui/src/components/comment-node.tsx | 881 +++++++++++++++-------------- ui/src/utils.ts | 4 + 2 files changed, 454 insertions(+), 431 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index a02a3cf8..0e62e6eb 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -27,6 +27,7 @@ import { pictshareAvatarThumbnail, showAvatars, setupTippy, + randomHsl, } from '../utils'; import moment from 'moment'; import { MomentTime } from './moment-time'; @@ -54,6 +55,7 @@ interface CommentNodeState { score: number; upvotes: number; downvotes: number; + borderColor: string; } interface CommentNodeProps { @@ -92,6 +94,7 @@ export class CommentNode extends Component { score: this.props.node.comment.score, upvotes: this.props.node.comment.upvotes, downvotes: this.props.node.comment.downvotes, + borderColor: randomHsl(), }; constructor(props: any, context: any) { @@ -116,7 +119,7 @@ export class CommentNode extends Component { return (
                  { className={`details comment-node mb-1 ${ this.isCommentNew ? 'mark' : '' }`} + style={ + !this.props.noIndent && + `border-left: 1px solid; border-color: ${this.state.borderColor} !important` + } > -
                    -
                  • - - {node.comment.creator_avatar && showAvatars() && ( - - )} - {node.comment.creator_name} - -
                  • - {this.isMod && ( -
                  • - {i18n.t('mod')} -
                  • - )} - {this.isAdmin && ( -
                  • - {i18n.t('admin')} -
                  • - )} - {this.isPostCreator && ( -
                  • - {i18n.t('creator')} -
                  • - )} - {(node.comment.banned_from_community || node.comment.banned) && ( -
                  • - {i18n.t('banned')} -
                  • - )} -
                  • - +
                    +
                    • - - - + + {node.comment.creator_avatar && showAvatars() && ( + + )} + {node.comment.creator_name} + +
                    • + {this.isMod && ( +
                    • + {i18n.t('mod')} +
                    • + )} + {this.isAdmin && ( +
                    • + {i18n.t('admin')} +
                    • + )} + {this.isPostCreator && ( +
                    • + {i18n.t('creator')} +
                    • + )} + {(node.comment.banned_from_community || node.comment.banned) && ( +
                    • + {i18n.t('banned')} +
                    • + )} +
                    • + +
                    • + + + + + {this.state.score} + +
                    • +
                    • + + - {this.state.score} + {this.state.upvotes} +
                    • +
                    • + + + + {this.state.downvotes} +
                    • +
                      + {this.props.showCommunity && ( +
                    • + {i18n.t('to')} + + {node.comment.community_name} + +
                    • + )} +
                    • +
                    • + +
                    • - - - - {this.state.upvotes} -
                    • -
                    • - - - - {this.state.downvotes} -
                    • - - {this.props.showCommunity && ( -
                    • - {i18n.t('to')} - - {node.comment.community_name} - -
                    • - )} -
                    • -
                    • - - - -
                    • -
                    • -
                      - {this.state.collapsed ? ( - - - - ) : ( - - - - )} -
                      -
                    • -
                    - {this.state.showEdit && ( - - )} - {!this.state.showEdit && !this.state.collapsed && ( -
                    - {this.state.viewSource ? ( -
                    {this.commentUnlessRemoved}
                    - ) : (
                    - )} -
                      - {this.props.markable && ( -
                    • - - - - - -
                    • - )} - {UserService.Instance.user && !this.props.viewOnly && ( - <> -
                    • - -
                    • - {WebSocketService.Instance.site.enable_downvotes && ( -
                    • - -
                    • + className="unselectable pointer text-monospace" + onClick={linkEvent(this, this.handleCommentCollapse)} + > + {this.state.collapsed ? ( + + + + ) : ( + + + + )} +
                    + +
                  + {this.state.showEdit && ( + + )} + {!this.state.showEdit && !this.state.collapsed && ( +
                  + {this.state.viewSource ? ( +
                  {this.commentUnlessRemoved}
                  + ) : ( +
                  + )} +
                    + {this.props.markable && (
                  • - - + +
                  • - {!this.myComment && ( + )} + {UserService.Instance.user && !this.props.viewOnly && ( + <>
                  • - - + - +
                  • - )} -
                  • - - - - - -
                  • - {!this.state.showAdvanced ? ( + {WebSocketService.Instance.site.enable_downvotes && ( +
                  • + +
                  • + )}
                  • - +
                  • - ) : ( - <> + {!this.myComment && ( +
                  • + + + + + +
                  • + )} +
                  • + + + + + +
                  • + {!this.state.showAdvanced ? (
                  • - - + +
                  • -
                  • - - - - - -
                  • - {this.myComment && ( - <> -
                  • -
                  • - - - - - -
                  • -
                  • - - - - - -
                  • - - )} - {/* Admins and mods can remove comments */} - {(this.canMod || this.canAdmin) && ( - <> -
                  • - {!node.comment.removed ? ( - - {i18n.t('remove')} - - ) : ( - - {i18n.t('restore')} - + ) : ( + <> +
                  • + - - )} - {/* Mods can ban from community, and appoint as mods to community */} - {this.canMod && ( - <> - {!this.isMod && ( + data-tippy-content={ + node.comment.saved + ? i18n.t('unsave') + : i18n.t('save') + } + > + + + + +
                  • +
                  • + + + + + +
                  • + {this.myComment && ( + <> +
                  • - {!node.comment.banned_from_community ? ( + + + + + +
                  • +
                  • + + + + + +
                  • + + )} + {/* Admins and mods can remove comments */} + {(this.canMod || this.canAdmin) && ( + <> +
                  • + {!node.comment.removed ? ( - {i18n.t('ban')} + {i18n.t('remove')} ) : ( - {i18n.t('unban')} + {i18n.t('restore')} )}
                  • - )} - {!node.comment.banned_from_community && ( + + )} + {/* Mods can ban from community, and appoint as mods to community */} + {this.canMod && ( + <> + {!this.isMod && ( +
                  • + {!node.comment.banned_from_community ? ( + + {i18n.t('ban')} + + ) : ( + + {i18n.t('unban')} + + )} +
                  • + )} + {!node.comment.banned_from_community && ( +
                  • + {!this.state.showConfirmAppointAsMod ? ( + + {this.isMod + ? i18n.t('remove_as_mod') + : i18n.t('appoint_as_mod')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
                  • + )} + + )} + {/* Community creators and admins can transfer community to another mod */} + {(this.amCommunityCreator || this.canAdmin) && + this.isMod && (
                  • - {!this.state.showConfirmAppointAsMod ? ( + {!this.state.showConfirmTransferCommunity ? ( - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} + {i18n.t('transfer_community')} ) : ( <> @@ -492,7 +553,7 @@ export class CommentNode extends Component { class="pointer d-inline-block mr-1" onClick={linkEvent( this, - this.handleAddModToCommunity + this.handleTransferCommunity )} > {i18n.t('yes')} @@ -501,7 +562,8 @@ export class CommentNode extends Component { class="pointer d-inline-block" onClick={linkEvent( this, - this.handleCancelConfirmAppointAsMod + this + .handleCancelShowConfirmTransferCommunity )} > {i18n.t('no')} @@ -510,21 +572,89 @@ export class CommentNode extends Component { )}
                  • )} - - )} - {/* Community creators and admins can transfer community to another mod */} - {(this.amCommunityCreator || this.canAdmin) && - this.isMod && ( + {/* Admins can ban from all, and appoint other admins */} + {this.canAdmin && ( + <> + {!this.isAdmin && ( +
                  • + {!node.comment.banned ? ( + + {i18n.t('ban_from_site')} + + ) : ( + + {i18n.t('unban_from_site')} + + )} +
                  • + )} + {!node.comment.banned && ( +
                  • + {!this.state.showConfirmAppointAsAdmin ? ( + + {this.isAdmin + ? i18n.t('remove_as_admin') + : i18n.t('appoint_as_admin')} + + ) : ( + <> + + {i18n.t('are_you_sure')} + + + {i18n.t('yes')} + + + {i18n.t('no')} + + + )} +
                  • + )} + + )} + {/* Site Creator can transfer to another admin */} + {this.amSiteCreator && this.isAdmin && (
                  • - {!this.state.showConfirmTransferCommunity ? ( + {!this.state.showConfirmTransferSite ? ( - {i18n.t('transfer_community')} + {i18n.t('transfer_site')} ) : ( <> @@ -535,7 +665,7 @@ export class CommentNode extends Component { class="pointer d-inline-block mr-1" onClick={linkEvent( this, - this.handleTransferCommunity + this.handleTransferSite )} > {i18n.t('yes')} @@ -544,8 +674,7 @@ export class CommentNode extends Component { class="pointer d-inline-block" onClick={linkEvent( this, - this - .handleCancelShowConfirmTransferCommunity + this.handleCancelShowConfirmTransferSite )} > {i18n.t('no')} @@ -554,124 +683,14 @@ export class CommentNode extends Component { )}
                  • )} - {/* Admins can ban from all, and appoint other admins */} - {this.canAdmin && ( - <> - {!this.isAdmin && ( -
                  • - {!node.comment.banned ? ( - - {i18n.t('ban_from_site')} - - ) : ( - - {i18n.t('unban_from_site')} - - )} -
                  • - )} - {!node.comment.banned && ( -
                  • - {!this.state.showConfirmAppointAsAdmin ? ( - - {this.isAdmin - ? i18n.t('remove_as_admin') - : i18n.t('appoint_as_admin')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
                  • - )} - - )} - {/* Site Creator can transfer to another admin */} - {this.amSiteCreator && this.isAdmin && ( -
                  • - {!this.state.showConfirmTransferSite ? ( - - {i18n.t('transfer_site')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
                  • - )} - - )} - - )} -
                  -
                  - )} + + )} + + )} +
                +
          + )} +
          {this.state.showRemoveDialog && ( Date: Thu, 5 Mar 2020 00:02:08 -0500 Subject: [PATCH 21/27] Changing user names to bold text-body. Removing color lines on first comment. --- ui/src/components/comment-node.tsx | 9 +++++++-- ui/src/components/main.tsx | 9 ++++++--- ui/src/components/modlog.tsx | 2 +- ui/src/components/post-listing.tsx | 5 ++++- ui/src/components/sidebar.tsx | 5 ++++- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 0e62e6eb..9b476c7e 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -129,14 +129,19 @@ export class CommentNode extends Component { }`} style={ !this.props.noIndent && + this.props.node.comment.parent_id && `border-left: 1px solid; border-color: ${this.state.borderColor} !important` } > -
          +
          • {node.comment.creator_avatar && showAvatars() && ( diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index ee028151..b772bd87 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -184,7 +184,7 @@ export class Main extends Component {
            # - + # @@ -222,7 +222,7 @@ export class Main extends Component {
            # - + # @@ -317,7 +317,10 @@ export class Main extends Component {
          • {i18n.t('admins')}:
          • {this.state.siteRes.admins.map(admin => (
          • - + {admin.avatar && showAvatars() && ( {
            {this.state.communityName && ( /c/{this.state.communityName}{' '} diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index f8f19448..0441bab8 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -425,7 +425,10 @@ export class PostListing extends Component {
            • {i18n.t('by')} - + {post.creator_avatar && showAvatars() && ( {
            • {i18n.t('mods')}:
            • {this.props.moderators.map(mod => (
            • - + {mod.avatar && showAvatars() && ( Date: Thu, 5 Mar 2020 00:39:22 -0500 Subject: [PATCH 22/27] Change post-listing vote colors. --- ui/src/components/post-listing.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 0441bab8..bfa58a73 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -469,7 +469,7 @@ export class PostListing extends Component {
            • - + @@ -477,7 +477,7 @@ export class PostListing extends Component {
            • - + From 1a9c1677d3a06032f360e21ae2ab58cebc5128c6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 5 Mar 2020 10:02:23 -0500 Subject: [PATCH 23/27] Adding hr separator for top level comment groups on mobile. --- ui/src/components/comment-node.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 9b476c7e..19c83423 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -122,6 +122,9 @@ export class CommentNode extends Component { node.comment.parent_id && !this.props.noIndent ? 'ml-2' : '' }`} > + {!node.comment.parent_id && !this.props.noIndent && ( +
              + )}
              { style={ !this.props.noIndent && this.props.node.comment.parent_id && - `border-left: 1px solid; border-color: ${this.state.borderColor} !important` + `border-left: 1px ${this.state.borderColor} solid !important` } >
              { )}
              + {/* end of details */} {this.state.showRemoveDialog && ( Date: Thu, 5 Mar 2020 10:18:48 -0500 Subject: [PATCH 24/27] Thinner blockquotes. Corrected user on private messaging. --- ui/assets/css/main.css | 2 +- ui/src/components/private-message-form.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 64f086b3..53237793 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -117,7 +117,7 @@ } blockquote { - border-left: 3px solid #ccc; + border-left: 1px solid var(--secondary); margin: 0.5em 5px; padding: 0.1em 5px; } diff --git a/ui/src/components/private-message-form.tsx b/ui/src/components/private-message-form.tsx index 13b4d2ea..90b3b6e1 100644 --- a/ui/src/components/private-message-form.tsx +++ b/ui/src/components/private-message-form.tsx @@ -126,7 +126,7 @@ export class PrivateMessageForm extends Component< {this.state.recipient && (
              {this.state.recipient.avatar && showAvatars() && ( From 858366c57b959c0b2bc6e2a43ca194084a1ff0d8 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 5 Mar 2020 15:10:46 -0500 Subject: [PATCH 25/27] Proper comment-node depth coloring. --- ui/src/components/comment-node.tsx | 6 ++++-- ui/src/components/post.tsx | 15 +++++++++++++-- ui/src/interfaces.ts | 1 + ui/src/utils.ts | 2 ++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 19c83423..db3c589d 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -27,7 +27,7 @@ import { pictshareAvatarThumbnail, showAvatars, setupTippy, - randomHsl, + colorList, } from '../utils'; import moment from 'moment'; import { MomentTime } from './moment-time'; @@ -94,7 +94,9 @@ export class CommentNode extends Component { score: this.props.node.comment.score, upvotes: this.props.node.comment.upvotes, downvotes: this.props.node.comment.downvotes, - borderColor: randomHsl(), + borderColor: this.props.node.comment.depth + ? colorList[this.props.node.comment.depth % colorList.length] + : colorList[0], }; constructor(props: any, context: any) { diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index faee23ed..e6b4a206 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -311,16 +311,27 @@ export class Post extends Component { } let tree: Array = []; for (let comment of this.state.comments) { + let child = map.get(comment.id); if (comment.parent_id) { - map.get(comment.parent_id).children.push(map.get(comment.id)); + let parent_ = map.get(comment.parent_id); + parent_.children.push(child); } else { - tree.push(map.get(comment.id)); + tree.push(child); } + + this.setDepth(child); } return tree; } + setDepth(node: CommentNodeI, i: number = 0): void { + for (let child of node.children) { + child.comment.depth = i; + this.setDepth(child, i + 1); + } + } + commentsTree() { let nodes = this.buildCommentsTree(); return ( diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 5baadb17..eb58ca11 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -208,6 +208,7 @@ export interface Comment { saved?: boolean; user_mention_id?: number; // For mention type recipient_id?: number; + depth?: number; } export interface Category { diff --git a/ui/src/utils.ts b/ui/src/utils.ts index bdd629c7..058be6ae 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -768,6 +768,8 @@ export function postSort( } } +export const colorList: Array = [...Array(10)].map(() => randomHsl()); + export function randomHsl() { return `hsla(${Math.random() * 360}, 100%, 50%, 1)`; } From 08af66ba28f38339a5ae655493d3ab299508d910 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 6 Mar 2020 09:55:32 -0500 Subject: [PATCH 26/27] Some comment-node additions - Hiding extra vote counts if no downvotes. - Showing numbers on actions if there are. --- ui/assets/css/main.css | 2 +- ui/src/components/comment-node.tsx | 90 ++++++++++++++---------------- ui/src/components/post.tsx | 2 +- ui/src/utils.ts | 15 ++++- 4 files changed, 58 insertions(+), 51 deletions(-) diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 53237793..b458a9d2 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -117,7 +117,7 @@ } blockquote { - border-left: 1px solid var(--secondary); + border-left: 2px solid var(--secondary); margin: 0.5em 5px; padding: 0.1em 5px; } diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index db3c589d..524367bc 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -125,7 +125,10 @@ export class CommentNode extends Component { }`} > {!node.comment.parent_id && !this.props.noIndent && ( -
              + <> +
              +
              + )}
              { style={ !this.props.noIndent && this.props.node.comment.parent_id && - `border-left: 1px ${this.state.borderColor} solid !important` + `border-left: 2px ${this.state.borderColor} solid !important` } >
              { {i18n.t('banned')}
            • )} -
            • - -
            • - - - - - {this.state.score} - -
            • -
            • - - - - {this.state.upvotes} -
            • -
            • - - - - {this.state.downvotes} -
            • -
              {this.props.showCommunity && (
            • {i18n.t('to')} @@ -219,6 +194,21 @@ export class CommentNode extends Component {
            • )}
            • +
            • + + + + + {this.state.score} + +
            • +
            • @@ -230,11 +220,11 @@ export class CommentNode extends Component { onClick={linkEvent(this, this.handleCommentCollapse)} > {this.state.collapsed ? ( - + ) : ( - + )} @@ -292,9 +282,12 @@ export class CommentNode extends Component { onClick={linkEvent(node, this.handleCommentUpvote)} data-tippy-content={i18n.t('upvote')} > - + + {this.state.upvotes !== this.state.score && ( + {this.state.upvotes} + )}
            • {WebSocketService.Instance.site.enable_downvotes && ( @@ -311,9 +304,12 @@ export class CommentNode extends Component { )} data-tippy-content={i18n.t('downvote')} > - + + {this.state.upvotes !== this.state.score && ( + {this.state.downvotes} + )} )} @@ -328,19 +324,6 @@ export class CommentNode extends Component { - {!this.myComment && ( -
            • - - - - - -
            • - )}
            • {
            • ) : ( <> + {!this.myComment && ( +
            • + + + + + +
            • + )}
            • { sortRadios() { return ( -
              +
            • +
            • -
            • - - - - - {this.state.upvotes} - -
            • -
            • - - - - - {this.state.downvotes} - -
            • +
            • + {this.state.upvotes !== this.state.score && ( + <> +
            • + + + + + {this.state.upvotes} + +
            • +
            • + + + + + {this.state.downvotes} + +
            • +
            • + + )}