Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement unanswered comment highlight at user comments list #660

Merged
merged 2 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -736,8 +736,8 @@ module.exports = {
'no-underscore-dangle': [2, { 'allow': [
'_id', '__get__', '__set__', '__RewireAPI__', '__Rewire__', '__ResetDependency__', '__GetDependency__',
] }],
// Max assertions is 10 and warning rather than error.
'jest/max-expects': [1, { 'max': 10 }],
// Max assertions is 20 and warning rather than error.
'jest/max-expects': [1, { 'max': 20 }],
// We are not using TypeScript
'jest/no-untyped-mock-factory': 0,
},
Expand Down
121 changes: 121 additions & 0 deletions controllers/__tests__/comment.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* Copyright: The PastVu contributors.
* GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl.txt)
*/

import { CommentN } from '../../models/Comment';
import admin from '../admin';
import comment from '../comment';
import testHelpers from '../../tests/testHelpers';

describe('comment', () => {
beforeEach(async () => {
// Mock non-registerd user handshake.
admin.handshake = { 'usObj': { 'isAdmin': true } };
});

afterEach(() => {
// Delete handshake.
delete admin.handshake;
});

describe('create for news', () => {
let news;

beforeEach(async () => {
const data = { pdate: new Date(), 'title': 'Test news', 'txt': 'Test news content' };

({ news } = await admin.saveOrCreateNews(data));

const user = await testHelpers.createUser({ login: 'user1', pass: 'pass1' });

// Mock non-registered user handshake.
comment.handshake = { 'usObj': { 'isAdmin': true, 'registered': true, user } };
});

afterEach(() => {
// Delete handshake.
delete comment.handshake;
});

it('create', async () => {
expect.assertions(3);

const data = { txt: 'news comment', type: 'news', obj: news.cid };

// Create two comments.
const result = await comment.create(data);

expect(result.comment.txt).toMatch(data.txt);
expect(result.comment.user).toMatch('user1');

await expect(CommentN.count({ obj: news })).resolves.toBe(1);
});
});

describe('retrive', () => {
let news;

beforeEach(async () => {
const data = { pdate: new Date(), 'title': 'Test news', 'txt': 'Test news content' };

({ news } = await admin.saveOrCreateNews(data));

const user = await testHelpers.createUser({ login: 'user1', pass: 'pass1' });

// Mock non-registered user handshake.
comment.handshake = { 'usObj': { 'isAdmin': true, 'registered': true, user } };
});

afterEach(() => {
// Delete handshake.
delete comment.handshake;
});

it('give news comments for user', async () => {
expect.assertions(17);

const data = { txt: 'news comment', type: 'news', obj: news.cid };

// Create 4 comments.
const comment0 = await comment.create(data);
const comment1 = await comment.create(data);

data.parent = comment1.comment.cid;
data.level = comment1.comment.level + 1;

const comment2 = await comment.create(data);

data.parent = comment2.comment.cid;
data.level = comment2.comment.level + 1;

const comment3 = await comment.create(data);

// Sanity check.
await expect(CommentN.count({ obj: news })).resolves.toBe(4);

const comments = await comment.giveForUser({ login: 'user1', type: 'news' });

expect(comments.type).toMatch('news');
expect(comments.countActive).toBe(4);
expect(comments.objs[news.cid].cid).toStrictEqual(news.cid);
expect(comments.objs[news.cid].ccount).toBe(4);
// Comment 0 - no child, waits answer.
expect(comments.comments[3].cid).toStrictEqual(comment0.comment.cid);
expect(comments.comments[3].hasChild).toBeFalsy();
expect(comments.comments[3].waitsAnswer).toBeTruthy();
// Comment 1 - has child, does not wait answer.
expect(comments.comments[2].cid).toStrictEqual(comment1.comment.cid);
expect(comments.comments[2].hasChild).toBeTruthy();
expect(comments.comments[2].waitsAnswer).toBeFalsy();
// Comment 2 - has child, does not wait answer.
expect(comments.comments[1].cid).toStrictEqual(comment2.comment.cid);
expect(comments.comments[1].hasChild).toBeTruthy();
expect(comments.comments[1].waitsAnswer).toBeFalsy();
// Comment 3 - no child, waits answer.
expect(comments.comments[0].cid).toStrictEqual(comment3.comment.cid);
expect(comments.comments[0].hasChild).toBeFalsy();
expect(comments.comments[0].waitsAnswer).toBeTruthy();
});
});
});
56 changes: 36 additions & 20 deletions controllers/comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,9 +722,36 @@ async function giveForUser({ login, page = 1, type = 'photo', active = true, del
}

const fields = { _id: 0, lastChanged: 1, cid: 1, obj: 1, stamp: 1, txt: 1, 'del.origin': 1 };
const options = { lean: true, sort: { stamp: -1 }, skip: page * commentsUserPerPage, limit: commentsUserPerPage };
const options = { sort: { stamp: -1 }, skip: page * commentsUserPerPage, limit: commentsUserPerPage };

comments = await commentModel.find(query, fields, options).exec();
if (!iAm.registered) {
comments = await commentModel.find(query, fields, options).lean().exec();
} else {
fields.hasChild = 1;
comments = await commentModel.aggregate([
{
'$match': query,
},
{
'$lookup': {
'from': commentModel.collection.collectionName,
'localField': 'cid',
'foreignField': 'parent',
'as': 'children',
},
},
{
'$addFields': {
'hasChild': {
$gt: [{ $size: '$children' }, 0],
},
},
},
{
'$project': fields,
},
]).sort(options.sort).skip(options.skip).limit(options.limit).exec();
}
}

if (_.isEmpty(comments)) {
Expand Down Expand Up @@ -757,18 +784,6 @@ async function giveForUser({ login, page = 1, type = 'photo', active = true, del

if (type === 'photo' && iAm.registered) {
await this.call('photo.fillPhotosProtection', { photos: objs, setMyFlag: true });

for (const obj of objs) {
objFormattedHashCid[obj.cid] = objFormattedHashId[obj._id] = obj;
obj._id = undefined;
obj.user = undefined;
obj.mime = undefined;
}
} else {
for (const obj of objs) {
objFormattedHashCid[obj.cid] = objFormattedHashId[obj._id] = obj;
obj._id = undefined;
}
}

for (const obj of objs) {
Expand All @@ -780,6 +795,9 @@ async function giveForUser({ login, page = 1, type = 'photo', active = true, del

// For each comment check object exists and assign to comment its cid
for (const comment of comments) {
// Mark those awaiting response.
comment.waitsAnswer = comment.hasChild !== undefined && !comment.hasChild;

const obj = objFormattedHashId[comment.obj];

if (obj !== undefined) {
Expand Down Expand Up @@ -926,7 +944,7 @@ async function create(data) {
throw obj.nocomments ? new NoticeError(constantsError.COMMENT_NOT_ALLOWED) : new AuthorizationError();
}

if (data.parent && (!parent || parent.del || parent.level >= 9 || data.level !== (parent.level || 0) + 1)) {
if (data.parent && (!parent || parent.del || parent.level >= 9 || data.level !== parent.level + 1)) {
throw new NoticeError(constantsError.COMMENT_WRONG_PARENT);
}

Expand All @@ -952,9 +970,11 @@ async function create(data) {
}
}

comment.level = data.level ?? 0;

if (data.parent) {
comment.parent = data.parent;
comment.level = data.level;
comment.level = data.level ?? parent.level + 1;
}

if (fragAdded) {
Expand Down Expand Up @@ -998,10 +1018,6 @@ async function create(data) {
comment.obj = objCid;
comment.can = {};

if (comment.level === undefined) {
comment.level = 0;
}

session.emitUser({ usObj: iAm, excludeSocket: socket });
subscrController.commentAdded(obj._id, iAm.user, stamp);

Expand Down
8 changes: 0 additions & 8 deletions public/style/comment/comments.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@import '../_vars.less';
@import '../bs/variables.less';
@import '../bs/mixins.less';
@import '../bs/badges.less';

@headColor: #677A8F;
@headColorHover: @MainBlueColor;
Expand Down Expand Up @@ -136,13 +135,6 @@

.levelLooping(0, 58);

.badge {
font-size: 11px;
font-weight: normal;
color: #f2f2f2;
background-color: rgba(85, 85, 85, 80%);
}

&.isnew {
padding-left: 5px;
border-width: 0 0 0 1px;
Expand Down
9 changes: 9 additions & 0 deletions public/style/common.less
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import '_vars.less';
@import 'fonts/fontU.less';
@import 'bs/bootstrap.less';
@import 'bs/badges.less';

@-webkit-keyframes fadeIn {
0% { opacity: 0; }
Expand Down Expand Up @@ -480,6 +481,14 @@ body {
}
}

// Badges
.badge-latest {
font-size: 11px;
font-weight: normal;
color: #f2f2f2;
background-color: rgba(85, 85, 85, 80%);
}

// Tooltip
.tltp {
position: absolute;
Expand Down
10 changes: 9 additions & 1 deletion views/module/user/comments.pug
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
.dotDelimeter ·
.commentChanged(title="Показать историю изменений", data-bind="text: ($data.del ? 'Удален ' : 'Изменен ') + moment($data.lastChanged).calendar().toLowerCase(), click: function () {$parent.showHistory($data.obj.cid, $data.cid)}")
// /ko
//ko if: $data.waitsAnswer
.dotDelimeter ·
.badge.badge-latest Ждёт ответа
// /ko
a.commentText(data-bind="attr: {href: $data.link}, html: $data.txt")
| </script>

Expand All @@ -58,6 +62,10 @@
.dotDelimeter ·
.commentChanged(title="Показать историю изменений", data-bind="text: ($data.del ? 'Удален ' : 'Изменен ') + moment($data.lastChanged).calendar().toLowerCase(), click: function () {$parent.showHistory($data.obj.cid, $data.cid)}")
// /ko
//ko if: $data.waitsAnswer
.dotDelimeter ·
.badge.badge-latest Ждёт ответа
// /ko
a.commentText(style="margin-left:29px", data-bind="attr: {href: $data.link}, html: $data.txt")
| </script>

Expand All @@ -70,4 +78,4 @@
// /ko
li.edge(data-bind="css: {disabled: !pageHasNext()}"): a(data-bind="attr: {href: pageUrl() + '/' + (page() + 1) + pageQuery()}", title="Следующая страница") &raquo;
li.edge(data-bind="css: {disabled: page() === pageLast()}"): a(data-bind="attr: {href: pageUrl() + '/' + pageLast() + pageQuery()}", title="Последняя страница") &raquo;&raquo;
| </script>
| </script>