blob: 121e64be12c61d4027c660aaa513d382e0e2d0fc [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001// Copyright 2019 The Chromium Authors
Copybara854996b2021-09-07 19:36:02 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {cache} from 'lit-html/directives/cache.js';
6import {LitElement, html, css} from 'lit-element';
7
8import '../../chops/chops-button/chops-button.js';
9import './mr-comment.js';
10import {connectStore} from 'reducers/base.js';
11import * as userV0 from 'reducers/userV0.js';
12import * as ui from 'reducers/ui.js';
13import {userIsMember} from 'shared/helpers.js';
14import {SHARED_STYLES} from 'shared/shared-styles.js';
15
16/**
17 * `<mr-comment-list>`
18 *
19 * Display a list of Monorail comments.
20 *
21 */
22export class MrCommentList extends connectStore(LitElement) {
23 /** @override */
24 constructor() {
25 super();
26
27 this.commentsShownCount = 2;
28 this.comments = [];
29 this.headingLevel = 4;
30
31 this.focusId = null;
32
33 this.usersProjects = new Map();
34
35 this._hideComments = true;
36 }
37
38 /** @override */
39 static get properties() {
40 return {
41 commentsShownCount: {type: Number},
42 comments: {type: Array},
43 headingLevel: {type: Number},
44
45 focusId: {type: String},
46
47 usersProjects: {type: Object},
48
49 _hideComments: {type: Boolean},
50 };
51 }
52
53 /** @override */
54 stateChanged(state) {
55 this.focusId = ui.focusId(state);
56 this.usersProjects = userV0.projectsPerUser(state);
57 }
58
59 /** @override */
60 updated(changedProperties) {
61 super.updated(changedProperties);
62
63 if (!this._hideComments) return;
64
65 // If any hidden comment is focused, show all hidden comments.
66 const hiddenCount =
67 _hiddenCount(this.comments.length, this.commentsShownCount);
68 const hiddenComments = this.comments.slice(0, hiddenCount);
69 for (const comment of hiddenComments) {
70 if ('c' + comment.sequenceNum === this.focusId) {
71 this._hideComments = false;
72 break;
73 }
74 };
75 }
76
77 /** @override */
78 static get styles() {
79 return [SHARED_STYLES, css`
80 button.toggle {
81 background: none;
82 color: var(--chops-link-color);
83 border: 0;
84 border-bottom: var(--chops-normal-border);
85 border-top: var(--chops-normal-border);
86 width: 100%;
87 padding: 0.5em 8px;
88 text-align: left;
89 font-size: var(--chops-main-font-size);
90 }
91 button.toggle:hover {
92 cursor: pointer;
93 text-decoration: underline;
94 }
95 button.toggle[hidden] {
96 display: none;
97 }
98 .edit-slot {
99 margin-top: 3em;
100 }
101 `];
102 }
103
104 /** @override */
105 render() {
106 const hiddenCount =
107 _hiddenCount(this.comments.length, this.commentsShownCount);
108 return html`
109 <button @click=${this._toggleHide}
110 class="toggle"
111 ?hidden=${hiddenCount <= 0}>
112 ${this._hideComments ? 'Show' : 'Hide'}
113 ${hiddenCount}
114 older
115 ${hiddenCount == 1 ? 'comment' : 'comments'}
116 </button>
117 ${cache(this._hideComments ? '' :
118 html`${this.comments.slice(0, hiddenCount).map(
119 this.renderComment.bind(this))}`)}
120 ${this.comments.slice(hiddenCount).map(this.renderComment.bind(this))}
121 `;
122 }
123
124 /**
125 * Helper to render a single comment.
126 * @param {Comment} comment
127 * @return {TemplateResult}
128 */
129 renderComment(comment) {
130 const commenterIsMember = userIsMember(
131 comment.commenter, comment.projectName, this.usersProjects);
132 return html`
133 <mr-comment
134 .comment=${comment}
135 headingLevel=${this.headingLevel}
136 ?highlighted=${'c' + comment.sequenceNum === this.focusId}
137 ?commenterIsMember=${commenterIsMember}
138 ></mr-comment>`;
139 }
140
141 /**
142 * Hides or unhides comments that are hidden by default. For example,
143 * if an issue has 200 comments, the first 100 comments are shown initially,
144 * then the last 100 can be toggled to be shown.
145 * @private
146 */
147 _toggleHide() {
148 this._hideComments = !this._hideComments;
149 }
150}
151
152/**
153 * Computes how many comments the user is able to expand.
154 * @param {number} commentCount Total comments.
155 * @param {number} commentsShownCount The number of comments shown.
156 * @return {number} The number of hidden comments.
157 * @private
158 */
159function _hiddenCount(commentCount, commentsShownCount) {
160 return Math.max(commentCount - commentsShownCount, 0);
161}
162
163customElements.define('mr-comment-list', MrCommentList);