Update file list to use functions from comment API

This also adds an intermediate string formatting object for comment
string manipulation tasks that are going to be done in the patch range
select and diff file dropdown as well.

Change-Id: Idee3debd24c40bcc70a0ac27cf1218394d79b53c
This commit is contained in:
Becky Siegel
2017-11-09 13:21:28 -08:00
parent 57e4fb3b8a
commit 6cd3de4139
6 changed files with 297 additions and 191 deletions

View File

@@ -29,6 +29,7 @@ limitations under the License.
<link rel="import" href="../../shared/gr-linked-text/gr-linked-text.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
<link rel="import" href="../../shared/gr-count-string-formatter/gr-count-string-formatter.html">
<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
<link rel="import" href="../gr-file-list-constants.html">
@@ -266,17 +267,16 @@ limitations under the License.
</span>
<div class="comments desktop">
<span class="drafts">
[[_computeDraftsString(changeComments.drafts, patchRange.patchNum, file.__path)]]
[[_computeDraftsString(changeComments, patchRange.patchNum, file.__path)]]
</span>
[[_computeCommentsString(changeComments.comments, patchRange.patchNum, file.__path)]]
[[_computeUnresolvedString(changeComments.comments, changeComments.drafts, patchRange.patchNum, file.__path)]]
[[_computeCommentsString(changeComments, patchRange.patchNum, file.__path)]]
</div>
<div class="comments mobile">
<span class="drafts">
[[_computeDraftsStringMobile(changeComments.drafts, patchRange.patchNum,
[[_computeDraftsStringMobile(changeComments, patchRange.patchNum,
file.__path)]]
</span>
[[_computeCommentsStringMobile(changeComments.comments, patchRange.patchNum,
[[_computeCommentsStringMobile(changeComments, patchRange.patchNum,
file.__path)]]
</div>
<div class$="[[_computeClass('stats', file.__path)]]">

View File

@@ -309,89 +309,67 @@
this.$.diffCursor.handleDiffUpdate();
},
_computeCommentsString(comments, patchNum, path) {
return this._computeCountString(comments, patchNum, path, 'comment');
},
_computeDraftsString(drafts, patchNum, path) {
return this._computeCountString(drafts, patchNum, path, 'draft');
},
_computeDraftsStringMobile(drafts, patchNum, path) {
const draftCount = this._computeCountString(drafts, patchNum, path);
return draftCount ? draftCount + 'd' : '';
},
_computeCommentsStringMobile(comments, patchNum, path) {
const commentCount = this._computeCountString(comments, patchNum, path);
return commentCount ? commentCount + 'c' : '';
},
getCommentsForPath(comments, patchNum, path) {
return (comments[path] || []).filter(c => {
return this.patchNumEquals(c.patch_set, patchNum);
});
},
/**
* @param {!Array} comments
* @param {number} patchNum
* @param {string} path
* @param {string=} opt_noun
*/
_computeCountString(comments, patchNum, path, opt_noun) {
if (!comments) { return ''; }
const patchComments = this.getCommentsForPath(comments, patchNum, path);
const num = patchComments.length;
if (num === 0) { return ''; }
if (!opt_noun) { return num; }
const output = num + ' ' + opt_noun + (num > 1 ? 's' : '');
return output;
},
/**
* Computes a string counting the number of unresolved comment threads in a
* given file and path.
* Computes a string with the number of comments and unresolved comments.
*
* @param {!Object} comments
* @param {!Object} drafts
* @param {!Object} changeComments
* @param {number} patchNum
* @param {string} path
* @return {string}
*/
_computeUnresolvedString(comments, drafts, patchNum, path) {
const unresolvedNum = this.computeUnresolvedNum(
comments, drafts, patchNum, path);
return unresolvedNum === 0 ? '' : '(' + unresolvedNum + ' unresolved)';
_computeCommentsString(changeComments, patchNum, path) {
const unresolvedCount = changeComments.computeUnresolvedNum(patchNum,
path);
const commentCount = changeComments.computeCommentCount(patchNum, path);
const commentString = GrCountStringFormatter.computePluralString(
commentCount, 'comment');
const unresolvedString = GrCountStringFormatter.computeString(
unresolvedCount, 'unresolved');
return commentString +
// Add a space if both comments and unresolved
(commentString && unresolvedString ? ' ' : '') +
// Add parentheses around unresolved if it exists.
(unresolvedString ? `(${unresolvedString})` : '');
},
computeUnresolvedNum(comments, drafts, patchNum, path) {
comments = this.getCommentsForPath(comments, patchNum, path);
drafts = this.getCommentsForPath(drafts, patchNum, path);
comments = comments.concat(drafts);
/**
* Computes a string with the number of drafts.
*
* @param {!Object} changeComments
* @param {number} patchNum
* @param {string} path
* @return {string}
*/
_computeDraftsString(changeComments, patchNum, path) {
const draftCount = changeComments.computeDraftCount(patchNum, path);
return GrCountStringFormatter.computePluralString(draftCount, 'draft');
},
// Create an object where every comment ID is the key of an unresolved
// comment.
/**
* Computes a shortened string with the number of drafts.
*
* @param {!Object} changeComments
* @param {number} patchNum
* @param {string} path
* @return {string}
*/
_computeDraftsStringMobile(changeComments, patchNum, path) {
const draftCount = changeComments.computeDraftCount(patchNum, path);
return GrCountStringFormatter.computeShortString(draftCount, 'd');
},
const idMap = comments.reduce((acc, comment) => {
if (comment.unresolved) {
acc[comment.id] = true;
}
return acc;
}, {});
// Set false for the comments that are marked as parents.
for (const comment of comments) {
idMap[comment.in_reply_to] = false;
}
// The unresolved comments are the comments that still have true.
const unresolvedLeaves = Object.keys(idMap).filter(key => {
return idMap[key];
});
return unresolvedLeaves.length;
/**
* Computes a shortened string with the number of comments.
*
* @param {!Object} changeComments
* @param {number} patchNum
* @param {string} path
* @return {string}
*/
_computeCommentsStringMobile(changeComments, patchNum, path) {
const commentCount = changeComments.computeCommentCount(patchNum, path);
return GrCountStringFormatter.computeShortString(commentCount, 'c');
},
_computeReviewed(file, _reviewed) {

View File

@@ -348,6 +348,135 @@ limitations under the License.
}
});
test('comment filtering', () => {
element.changeComments._comments = {
'/COMMIT_MSG': [
{patch_set: 1, message: 'Done', updated: '2017-02-08 16:40:49'},
{patch_set: 1, message: 'oh hay', updated: '2017-02-09 16:40:49'},
{patch_set: 2, message: 'hello', updated: '2017-02-10 16:40:49'},
],
'myfile.txt': [
{patch_set: 1, message: 'good news!', updated: '2017-02-08 16:40:49'},
{patch_set: 2, message: 'wat!?', updated: '2017-02-09 16:40:49'},
{patch_set: 2, message: 'hi', updated: '2017-02-10 16:40:49'},
],
'unresolved.file': [
{
patch_set: 2,
message: 'wat!?',
updated: '2017-02-09 16:40:49',
id: '1',
unresolved: true,
},
{
patch_set: 2,
message: 'hi',
updated: '2017-02-10 16:40:49',
id: '2',
in_reply_to: '1',
unresolved: false,
},
{
patch_set: 2,
message: 'good news!',
updated: '2017-02-08 16:40:49',
id: '3',
unresolved: true,
},
],
};
element.changeComments._drafts = {
'/COMMIT_MSG': [
{
patch_set: 1,
message: 'hi',
updated: '2017-02-15 16:40:49',
id: '5',
unresolved: true,
},
{
patch_set: 1,
message: 'fyi',
updated: '2017-02-15 16:40:49',
id: '6',
unresolved: false,
},
],
'unresolved.file': [
{
patch_set: 1,
message: 'hi',
updated: '2017-02-11 16:40:49',
id: '4',
unresolved: false,
},
],
};
assert.equal(
element._computeCommentsString(element.changeComments, '1',
'/COMMIT_MSG', 'comment'), '2 comments (1 unresolved)');
assert.equal(
element._computeCommentsStringMobile(element.changeComments, '1'
, '/COMMIT_MSG'), '2c');
assert.equal(
element._computeDraftsString(element.changeComments, '1',
'unresolved.file'), '1 draft');
assert.equal(
element._computeDraftsStringMobile(element.changeComments, '1',
'unresolved.file'), '1d');
assert.equal(
element._computeCommentsString(element.changeComments, '1',
'myfile.txt', 'comment'), '1 comment');
assert.equal(
element._computeCommentsStringMobile(element.changeComments, '1',
'myfile.txt'), '1c');
assert.equal(
element._computeDraftsString(element.changeComments, '1',
'myfile.txt'), '');
assert.equal(
element._computeDraftsStringMobile(element.changeComments, '1',
'myfile.txt'), '');
assert.equal(
element._computeCommentsString(element.changeComments, '1',
'file_added_in_rev2.txt', 'comment'), '');
assert.equal(
element._computeCommentsStringMobile(element.changeComments, '1',
'file_added_in_rev2.txt'), '');
assert.equal(
element._computeDraftsString(element.changeComments, '1',
'file_added_in_rev2.txt'), '');
assert.equal(
element._computeDraftsStringMobile(element.changeComments, '1',
'file_added_in_rev2.txt'), '');
assert.equal(
element._computeCommentsString(element.changeComments, '2',
'/COMMIT_MSG', 'comment'), '1 comment');
assert.equal(
element._computeCommentsStringMobile(element.changeComments, '2',
'/COMMIT_MSG'), '1c');
assert.equal(
element._computeDraftsString(element.changeComments, '1',
'/COMMIT_MSG'), '2 drafts');
assert.equal(
element._computeDraftsStringMobile(element.changeComments, '1',
'/COMMIT_MSG'), '2d');
assert.equal(
element._computeCommentsString(element.changeComments, '2',
'myfile.txt', 'comment'), '2 comments');
assert.equal(
element._computeCommentsStringMobile(element.changeComments, '2',
'myfile.txt'), '2c');
assert.equal(
element._computeDraftsStringMobile(element.changeComments, '2',
'myfile.txt'), '');
assert.equal(
element._computeCommentsString(element.changeComments, '2',
'file_added_in_rev2.txt', 'comment'), '');
assert.equal(element._computeCommentsString(element.changeComments, '2',
'unresolved.file', 'comment'), '3 comments (1 unresolved)');
});
suite('keyboard shortcuts', () => {
setup(() => {
element._files = [
@@ -526,119 +655,6 @@ limitations under the License.
});
});
test('comment filtering', () => {
const comments = {
'/COMMIT_MSG': [
{patch_set: 1, message: 'Done', updated: '2017-02-08 16:40:49'},
{patch_set: 1, message: 'oh hay', updated: '2017-02-09 16:40:49'},
{patch_set: 2, message: 'hello', updated: '2017-02-10 16:40:49'},
],
'myfile.txt': [
{patch_set: 1, message: 'good news!', updated: '2017-02-08 16:40:49'},
{patch_set: 2, message: 'wat!?', updated: '2017-02-09 16:40:49'},
{patch_set: 2, message: 'hi', updated: '2017-02-10 16:40:49'},
],
'unresolved.file': [
{
patch_set: 2,
message: 'wat!?',
updated: '2017-02-09 16:40:49',
id: '1',
unresolved: true,
},
{
patch_set: 2,
message: 'hi',
updated: '2017-02-10 16:40:49',
id: '2',
in_reply_to: '1',
unresolved: false,
},
{
patch_set: 2,
message: 'good news!',
updated: '2017-02-08 16:40:49',
id: '3',
unresolved: true,
},
],
};
const drafts = {
'unresolved.file': [
{
patch_set: 2,
message: 'hi',
updated: '2017-02-11 16:40:49',
id: '4',
in_reply_to: '3',
unresolved: false,
},
],
};
assert.equal(
element._computeCountString(comments, '1', '/COMMIT_MSG', 'comment'),
'2 comments');
assert.equal(
element._computeCommentsStringMobile(comments, '1', '/COMMIT_MSG'),
'2c');
assert.equal(
element._computeDraftsStringMobile(comments, '1', '/COMMIT_MSG'),
'2d');
assert.equal(
element._computeCountString(comments, '1', 'myfile.txt', 'comment'),
'1 comment');
assert.equal(
element._computeCommentsStringMobile(comments, '1', 'myfile.txt'),
'1c');
assert.equal(
element._computeDraftsStringMobile(comments, '1', 'myfile.txt'),
'1d');
assert.equal(
element._computeCountString(comments, '1',
'file_added_in_rev2.txt', 'comment'), '');
assert.equal(
element._computeCommentsStringMobile(comments, '1',
'file_added_in_rev2.txt'), '');
assert.equal(
element._computeDraftsStringMobile(comments, '1',
'file_added_in_rev2.txt'), '');
assert.equal(
element._computeCountString(comments, '2', '/COMMIT_MSG', 'comment'),
'1 comment');
assert.equal(
element._computeCommentsStringMobile(comments, '2', '/COMMIT_MSG'),
'1c');
assert.equal(
element._computeDraftsStringMobile(comments, '2', '/COMMIT_MSG'),
'1d');
assert.equal(
element._computeCountString(comments, '2', 'myfile.txt', 'comment'),
'2 comments');
assert.equal(
element._computeCommentsStringMobile(comments, '2', 'myfile.txt'),
'2c');
assert.equal(
element._computeDraftsStringMobile(comments, '2', 'myfile.txt'),
'2d');
assert.equal(
element._computeCountString(comments, '2',
'file_added_in_rev2.txt', 'comment'), '');
assert.equal(element._computeCountString(comments, '2',
'unresolved.file', 'comment'), '3 comments');
assert.equal(
element._computeUnresolvedString(comments, [], 2, 'myfile.txt'), '');
assert.equal(
element.computeUnresolvedNum(comments, [], 2, 'myfile.txt'), 0);
assert.equal(
element._computeUnresolvedString(comments, [], 2, 'unresolved.file'),
'(1 unresolved)');
assert.equal(
element.computeUnresolvedNum(comments, [], 2, 'unresolved.file'), 1);
assert.equal(
element._computeUnresolvedString(comments, drafts, 2,
'unresolved.file'), '');
});
test('computed properties', () => {
assert.equal(element._computeFileStatus('A'), 'A');
assert.equal(element._computeFileStatus(undefined), 'M');

View File

@@ -0,0 +1,57 @@
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
(function(window) {
'use strict';
const GrCountStringFormatter = window.GrCountStringFormatter || {};
/**
* Returns a count plus string that is pluralized when necessary.
*
* @param {number} count
* @param {string} noun
* @return {string}
*/
GrCountStringFormatter.computePluralString = function(count, noun) {
return this.computeString(count, noun) + (count > 1 ? 's' : '');
};
/**
* Returns a count plus string that is not pluralized.
*
* @param {number} count
* @param {string} noun
* @return {string}
*/
GrCountStringFormatter.computeString = function(count, noun) {
if (count === 0) { return ''; }
return count + ' ' + noun;
};
/**
* Returns a count plus arbitrary text.
*
* @param {number} count
* @param {string} text
* @return {string}
*/
GrCountStringFormatter.computeShortString = function(count, text) {
if (count === 0) { return ''; }
return count + text;
};
window.GrCountStringFormatter = GrCountStringFormatter;
})(window);
</script>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-count-string-formatter</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-count-string-formatter.html"/>
<script>
suite('gr-count-string-formatter tests', () => {
test('computeString', () => {
const noun = 'unresolved';
assert.equal(GrCountStringFormatter.computeString(0, noun), '');
assert.equal(GrCountStringFormatter.computeString(1, noun),
'1 unresolved');
assert.equal(GrCountStringFormatter.computeString(2, noun),
'2 unresolved');
});
test('computeShortString', () => {
const noun = 'c';
assert.equal(GrCountStringFormatter.computeShortString(0, noun), '');
assert.equal(GrCountStringFormatter.computeShortString(1, noun), '1c');
assert.equal(GrCountStringFormatter.computeShortString(2, noun), '2c');
});
test('computePluralString', () => {
const noun = 'comment';
assert.equal(GrCountStringFormatter.computePluralString(0, noun), '');
assert.equal(GrCountStringFormatter.computePluralString(1, noun),
'1 comment');
assert.equal(GrCountStringFormatter.computePluralString(2, noun),
'2 comments');
});
});
</script>

View File

@@ -33,6 +33,7 @@ const EXTERN_NAMES = [
'GrRangeNormalizer',
'GrReporting',
'GrReviewerUpdatesParser',
'GrCountStringFormatter',
'GrThemeApi',
'moment',
'page',