Browse Source

comments updates

pull/2/head
Rob Colbert 3 years ago
parent
commit
b7ff19dc5e
  1. 5
      app/models/comment.js
  2. 14
      app/views/comment/components/comment.pug
  3. 18
      app/views/comment/components/composer.pug
  4. 2
      app/views/comment/components/section.pug
  5. 9
      client/js/index.js
  6. 15
      client/js/site-app.js
  7. 85
      client/js/site-comments.js

5
app/models/comment.js

@ -18,6 +18,8 @@ const {
RESOURCE_TYPE_LIST, RESOURCE_TYPE_LIST,
CommentStats, CommentStats,
CommentStatsDefaults, CommentStatsDefaults,
ResourceStats,
ResourceStatsDefaults,
} = require(path.join(__dirname, 'lib', 'resource-stats.js')); } = require(path.join(__dirname, 'lib', 'resource-stats.js'));
const COMMENT_STATUS_LIST = [ const COMMENT_STATUS_LIST = [
@ -40,7 +42,8 @@ const CommentSchema = new Schema({
flags: { flags: {
isNSFW: { type: Boolean, default: false, required: true }, isNSFW: { type: Boolean, default: false, required: true },
}, },
stats: { type: CommentStats, default: CommentStatsDefaults, required: true }, resourceStats: { type: ResourceStats, default: ResourceStatsDefaults, required: true },
commentStats: { type: CommentStats, default: CommentStatsDefaults, required: true },
}); });
/* /*

14
app/views/comment/components/comment.pug

@ -28,7 +28,7 @@ mixin renderComment (comment, options)
a( a(
href="", href="",
data-comment-id= comment._id, data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].deleteComment(event);`, onclick=`return dtp.app.comments.deleteComment(event);`,
) Delete ) Delete
else if user else if user
li.uk-nav-header.no-select Moderation menu li.uk-nav-header.no-select Moderation menu
@ -38,7 +38,7 @@ mixin renderComment (comment, options)
data-resource-type= comment.resourceType, data-resource-type= comment.resourceType,
data-resource-id= resourceId, data-resource-id= resourceId,
data-comment-id= comment._id, data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].showReportCommentForm(event);`, onclick=`return dtp.app.comments.showReportCommentForm(event);`,
) Report ) Report
li li
a( a(
@ -46,7 +46,7 @@ mixin renderComment (comment, options)
data-resource-type= comment.resourceType, data-resource-type= comment.resourceType,
data-resource-id= resourceId, data-resource-id= resourceId,
data-comment-id= comment._id, data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].blockCommentAuthor(event);`, onclick=`return dtp.app.comments.blockCommentAuthor(event);`,
) Block author ) Block author
.uk-comment-body .uk-comment-body
@ -82,7 +82,7 @@ mixin renderComment (comment, options)
type="button", type="button",
data-comment-id= comment._id, data-comment-id= comment._id,
data-vote="up", data-vote="up",
onclick=`return dtp.app.comments['${options.name}'].submitCommentVote(event);`, onclick=`return dtp.app.comments.submitCommentVote(event);`,
title="Upvote this comment", title="Upvote this comment",
).uk-button.uk-button-link ).uk-button.uk-button-link
+renderLabeledIcon('fa-chevron-up', formatCount(comment.stats.upvoteCount)) +renderLabeledIcon('fa-chevron-up', formatCount(comment.stats.upvoteCount))
@ -91,7 +91,7 @@ mixin renderComment (comment, options)
type="button", type="button",
data-comment-id= comment._id, data-comment-id= comment._id,
data-vote="down", data-vote="down",
onclick=`return dtp.app.comments['${options.name}'].submitCommentVote(event);`, onclick=`return dtp.app.comments.submitCommentVote(event);`,
title="Downvote this comment", title="Downvote this comment",
).uk-button.uk-button-link ).uk-button.uk-button-link
+renderLabeledIcon('fa-chevron-down', formatCount(comment.stats.downvoteCount)) +renderLabeledIcon('fa-chevron-down', formatCount(comment.stats.downvoteCount))
@ -99,7 +99,7 @@ mixin renderComment (comment, options)
button( button(
type="button", type="button",
data-comment-id= comment._id, data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].openReplies(event);`, onclick=`return dtp.app.comments.openReplies(event);`,
title="Load replies to this comment", title="Load replies to this comment",
).uk-button.uk-button-link ).uk-button.uk-button-link
+renderLabeledIcon('fa-comment', formatCount(comment.stats.replyCount)) +renderLabeledIcon('fa-comment', formatCount(comment.stats.replyCount))
@ -107,7 +107,7 @@ mixin renderComment (comment, options)
button( button(
type="button", type="button",
data-comment-id= comment._id, data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].openReplyComposer(event);`, onclick=`return dtp.app.comments.openReplyComposer(event);`,
title="Write a reply to this comment", title="Write a reply to this comment",
).uk-button.uk-button-link ).uk-button.uk-button-link
+renderLabeledIcon('fa-reply', 'reply') +renderLabeledIcon('fa-reply', 'reply')

18
app/views/comment/components/composer.pug

@ -1,5 +1,10 @@
mixin renderCommentComposer (options = { }) mixin renderCommentComposer (formId, options = { })
form(method="POST", action= options.rootUrl, onsubmit="return dtp.app.submitForm(event, 'create-comment');").uk-form form(
id= formId,
method="POST",
action= options.rootUrl,
onsubmit="return dtp.app.submitForm(event, 'create-comment');",
).uk-form
if options.replyTo if options.replyTo
input(type="hidden", name="replyTo", value= options.replyTo) input(type="hidden", name="replyTo", value= options.replyTo)
@ -11,7 +16,8 @@ mixin renderCommentComposer (options = { })
rows="4", rows="4",
maxlength="3000", maxlength="3000",
placeholder="Enter comment", placeholder="Enter comment",
oninput=`return dtp.app.comments['${options.name}'].onCommentInput(event);`, data-form-id= formId,
oninput=`return dtp.app.comments.onCommentInput(event);`,
).uk-textarea.uk-resize-vertical ).uk-textarea.uk-resize-vertical
.uk-text-small .uk-text-small
div(uk-grid).uk-flex-between div(uk-grid).uk-flex-between
@ -22,15 +28,15 @@ mixin renderCommentComposer (options = { })
div(uk-grid).uk-flex-between.uk-grid-small div(uk-grid).uk-flex-between.uk-grid-small
.uk-width-expand .uk-width-expand
ul.uk-subnav ul.uk-subnav
li li.comment-emoji-picker
button( button(
type="button", type="button",
uk-tooltip="Add an emoji", uk-tooltip="Add an emoji",
).uk-button.dtp-button-default ).uk-button.dtp-button-default
span span
i.far.fa-smile i.far.fa-smile
#comment-emoji-picker(uk-drop={ mode: 'click' }) .comment-emoji-picker-drop(data-form-id= formId, uk-drop={ mode: 'click' })
.comment-emoji-picker .comment-emoji-picker-ui
div THIS IS THE EMOJI PICKER div THIS IS THE EMOJI PICKER
li(title="Not Safe For Work will hide your comment text by default") li(title="Not Safe For Work will hide your comment text by default")

2
app/views/comment/components/section.pug

@ -12,7 +12,7 @@ mixin renderCommentSection (options = { })
.uk-margin .uk-margin
+renderSectionTitle('Add a comment') +renderSectionTitle('Add a comment')
.uk-margin-small .uk-margin-small
+renderCommentComposer(composerOptions) +renderCommentComposer(composerOptions.name, composerOptions)
if featuredComment if featuredComment
.content-block(dtp-comments= `${options.name}-feature`, data-root-url= options.rootUrl) .content-block(dtp-comments= `${options.name}-feature`, data-root-url= options.rootUrl)

9
client/js/index.js

@ -12,6 +12,15 @@ import DtpSiteApp from './site-app.js';
import DtpWebLog from 'dtp/dtp-log.js'; import DtpWebLog from 'dtp/dtp-log.js';
import UIkit from 'uikit'; import UIkit from 'uikit';
/**
* Monkeypatch to count characters instead of .length's code point count.
* @returns character count of string
*/
String.prototype.charCount = function () {
const splat = [...this];
return splat.length;
};
window.addEventListener('load', async ( ) => { window.addEventListener('load', async ( ) => {
// application console log // application console log
dtp.log = new DtpWebLog(DTP_COMPONENT); dtp.log = new DtpWebLog(DTP_COMPONENT);

15
client/js/site-app.js

@ -43,26 +43,13 @@ export default class DtpSiteApp extends DtpApp {
}); });
this.chat = new SiteChat(this); this.chat = new SiteChat(this);
this.comments = new SiteComments(this);
this.initializeComments();
this.charts = { /* will hold rendered charts */ }; this.charts = { /* will hold rendered charts */ };
this.scrollToHash(); this.scrollToHash();
} }
initializeComments ( ) {
this.comments = { };
const containers = document.querySelectorAll('[dtp-comments]');
containers.forEach((container) => {
const name = container.getAttribute('dtp-comments');
const rootUrl = container.getAttribute('data-root-url');
this.log.info('initializeComments', 'initializing commenting scope', { name, rootUrl });
this.comments[name] = new SiteComments(this, container);
});
}
async scrollToHash ( ) { async scrollToHash ( ) {
const { hash } = window.location; const { hash } = window.location;
if (hash === '') { if (hash === '') {

85
client/js/site-comments.js

@ -12,38 +12,56 @@ import * as picmo from 'picmo';
export default class SiteComments { export default class SiteComments {
constructor (app, rootElement) { constructor (app) {
this.app = app; this.app = app;
this.log = new DtpLog({ name: 'Site Comments', slug: 'comments' }); this.log = new DtpLog({ name: 'Site Comments', slug: 'comments' });
this.createEmojiPickers();
}
createEmojiPickers ( ) {
const pickerContainers = document.querySelectorAll('li.comment-emoji-picker:not([data-initialized])');
for (const container of pickerContainers) {
const picker = { };
picker.drop = container.querySelector('.comment-emoji-picker-drop');
picker.ui = picker.drop.querySelector('.comment-emoji-picker-ui');
this.ui = { const formId = picker.drop.getAttribute('data-form-id');
input: rootElement.querySelector('#comment-content'), picker.form = document.querySelector(`form#${formId}`);
emojiPicker: rootElement.querySelector('#comment-emoji-picker'), picker.input = picker.form.querySelector(`textarea[data-form-id=${formId}]`);
characterCount: rootElement.querySelector('.comment-character-count'), picker.characterCount = picker.form.querySelector('span.comment-character-count');
};
if (this.ui.emojiPicker) { picker.picmo = picmo.createPicker({
this.ui.emojiPickerUI = this.ui.emojiPicker.querySelector('.comment-emoji-picker');
this.ui.picmo = picmo.createPicker({
emojisPerRow: 7, emojisPerRow: 7,
rootElement: this.ui.emojiPickerUI, rootElement: picker.ui,
theme: picmo.darkTheme, theme: picmo.darkTheme,
}); });
this.ui.picmo.addEventListener('emoji:select', this.onEmojiSelected.bind(this)); picker.picmo.addEventListener('emoji:select', this.onEmojiSelected.bind(this, picker));
this.ui.emojiPickerDrop = UIkit.drop(this.ui.emojiPicker); picker.emojiPickerDrop = UIkit.drop(picker.drop);
UIkit.util.on(this.ui.emojiPicker, 'show', ( ) => { UIkit.util.on(picker.drop, 'show', ( ) => {
this.log.info('SiteComments', 'showing emoji picker'); this.log.info('SiteComments', 'showing emoji picker');
this.ui.picmo.reset(); picker.picmo.reset();
}); });
} else {
UIkit.modal.alert('Comment section without an emoji picker defined'); container.setAttribute('data-initialized', true);
} }
} }
async onCommentInput (event) { async onCommentInput (event) {
this.ui.characterCount.textContent = numeral(event.target.value.length).format('0,0'); const target = event.currentTarget || event.target;
const formId = target.getAttribute('data-form-id');
if (!formId) { return; }
const form = document.getElementById(formId);
if (!form) { return; }
const label = form.querySelector('span.comment-character-count');
if (!label) { return; }
label.textContent = numeral(event.target.value.charCount()).format('0,0');
} }
async showReportCommentForm (event) { async showReportCommentForm (event) {
@ -177,6 +195,7 @@ export default class SiteComments {
try { try {
const response = await fetch(`/comment/${commentId}/replies`); const response = await fetch(`/comment/${commentId}/replies`);
this.app.processResponse(response); this.app.processResponse(response);
this.createEmojiPickers();
} catch (error) { } catch (error) {
UIkit.modal.alert(`Failed to load replies: ${error.message}`); UIkit.modal.alert(`Failed to load replies: ${error.message}`);
} }
@ -209,36 +228,38 @@ export default class SiteComments {
try { try {
const response = await fetch(`${rootUrl}?p=${nextPage}&buttonId=${buttonId}`); const response = await fetch(`${rootUrl}?p=${nextPage}&buttonId=${buttonId}`);
await this.app.processResponse(response); await this.app.processResponse(response);
this.createEmojiPickers();
} catch (error) { } catch (error) {
UIkit.modal.alert(`Failed to load more comments: ${error.message}`); UIkit.modal.alert(`Failed to load more comments: ${error.message}`);
} }
} }
async onEmojiSelected (event) { async onEmojiSelected (picker, event) {
this.ui.emojiPickerDrop.hide(false); picker.emojiPickerDrop.hide(false);
return this.insertContentAtCursor(event.emoji); await this.insertContentAtCursor(picker, event.emoji);
picker.characterCount.textContent = numeral(picker.input.value.charCount()).format('0,0');
} }
async insertContentAtCursor (content) { async insertContentAtCursor (picker, content) {
this.ui.input.focus(); picker.input.focus();
if (document.selection) { if (document.selection) {
let sel = document.selection.createRange(); let sel = document.selection.createRange();
sel.text = content; sel.text = content;
} else if (this.ui.input.selectionStart || (this.ui.input.selectionStart === 0)) { } else if (picker.input.selectionStart || (picker.input.selectionStart === 0)) {
let startPos = this.ui.input.selectionStart; let startPos = picker.input.selectionStart;
let endPos = this.ui.input.selectionEnd; let endPos = picker.input.selectionEnd;
let oldLength = this.ui.input.value.length; let oldLength = picker.input.value.length;
this.ui.input.value = picker.input.value =
this.ui.input.value.substring(0, startPos) + picker.input.value.substring(0, startPos) +
content + content +
this.ui.input.value.substring(endPos, this.ui.input.value.length); picker.input.value.substring(endPos, picker.input.value.length);
this.ui.input.selectionStart = startPos + (this.ui.input.value.length - oldLength); picker.input.selectionStart = startPos + (picker.input.value.length - oldLength);
this.ui.input.selectionEnd = this.ui.input.selectionStart; picker.input.selectionEnd = picker.input.selectionStart;
} else { } else {
this.ui.input.value += content; picker.input.value += content;
} }
} }
} }
Loading…
Cancel
Save