Browse Source

comments updates

develop^2
Rob Colbert 2 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,
CommentStats,
CommentStatsDefaults,
ResourceStats,
ResourceStatsDefaults,
} = require(path.join(__dirname, 'lib', 'resource-stats.js'));
const COMMENT_STATUS_LIST = [
@ -40,7 +42,8 @@ const CommentSchema = new Schema({
flags: {
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(
href="",
data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].deleteComment(event);`,
onclick=`return dtp.app.comments.deleteComment(event);`,
) Delete
else if user
li.uk-nav-header.no-select Moderation menu
@ -38,7 +38,7 @@ mixin renderComment (comment, options)
data-resource-type= comment.resourceType,
data-resource-id= resourceId,
data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].showReportCommentForm(event);`,
onclick=`return dtp.app.comments.showReportCommentForm(event);`,
) Report
li
a(
@ -46,7 +46,7 @@ mixin renderComment (comment, options)
data-resource-type= comment.resourceType,
data-resource-id= resourceId,
data-comment-id= comment._id,
onclick=`return dtp.app.comments['${options.name}'].blockCommentAuthor(event);`,
onclick=`return dtp.app.comments.blockCommentAuthor(event);`,
) Block author
.uk-comment-body
@ -82,7 +82,7 @@ mixin renderComment (comment, options)
type="button",
data-comment-id= comment._id,
data-vote="up",
onclick=`return dtp.app.comments['${options.name}'].submitCommentVote(event);`,
onclick=`return dtp.app.comments.submitCommentVote(event);`,
title="Upvote this comment",
).uk-button.uk-button-link
+renderLabeledIcon('fa-chevron-up', formatCount(comment.stats.upvoteCount))
@ -91,7 +91,7 @@ mixin renderComment (comment, options)
type="button",
data-comment-id= comment._id,
data-vote="down",
onclick=`return dtp.app.comments['${options.name}'].submitCommentVote(event);`,
onclick=`return dtp.app.comments.submitCommentVote(event);`,
title="Downvote this comment",
).uk-button.uk-button-link
+renderLabeledIcon('fa-chevron-down', formatCount(comment.stats.downvoteCount))
@ -99,7 +99,7 @@ mixin renderComment (comment, options)
button(
type="button",
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",
).uk-button.uk-button-link
+renderLabeledIcon('fa-comment', formatCount(comment.stats.replyCount))
@ -107,7 +107,7 @@ mixin renderComment (comment, options)
button(
type="button",
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",
).uk-button.uk-button-link
+renderLabeledIcon('fa-reply', 'reply')

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

@ -1,5 +1,10 @@
mixin renderCommentComposer (options = { })
form(method="POST", action= options.rootUrl, onsubmit="return dtp.app.submitForm(event, 'create-comment');").uk-form
mixin renderCommentComposer (formId, options = { })
form(
id= formId,
method="POST",
action= options.rootUrl,
onsubmit="return dtp.app.submitForm(event, 'create-comment');",
).uk-form
if options.replyTo
input(type="hidden", name="replyTo", value= options.replyTo)
@ -11,7 +16,8 @@ mixin renderCommentComposer (options = { })
rows="4",
maxlength="3000",
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-text-small
div(uk-grid).uk-flex-between
@ -22,15 +28,15 @@ mixin renderCommentComposer (options = { })
div(uk-grid).uk-flex-between.uk-grid-small
.uk-width-expand
ul.uk-subnav
li
li.comment-emoji-picker
button(
type="button",
uk-tooltip="Add an emoji",
).uk-button.dtp-button-default
span
i.far.fa-smile
#comment-emoji-picker(uk-drop={ mode: 'click' })
.comment-emoji-picker
.comment-emoji-picker-drop(data-form-id= formId, uk-drop={ mode: 'click' })
.comment-emoji-picker-ui
div THIS IS THE EMOJI PICKER
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
+renderSectionTitle('Add a comment')
.uk-margin-small
+renderCommentComposer(composerOptions)
+renderCommentComposer(composerOptions.name, composerOptions)
if featuredComment
.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 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 ( ) => {
// application console log
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.initializeComments();
this.comments = new SiteComments(this);
this.charts = { /* will hold rendered charts */ };
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 ( ) {
const { hash } = window.location;
if (hash === '') {

85
client/js/site-comments.js

@ -12,38 +12,56 @@ import * as picmo from 'picmo';
export default class SiteComments {
constructor (app, rootElement) {
constructor (app) {
this.app = app;
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 = {
input: rootElement.querySelector('#comment-content'),
emojiPicker: rootElement.querySelector('#comment-emoji-picker'),
characterCount: rootElement.querySelector('.comment-character-count'),
};
const formId = picker.drop.getAttribute('data-form-id');
picker.form = document.querySelector(`form#${formId}`);
picker.input = picker.form.querySelector(`textarea[data-form-id=${formId}]`);
picker.characterCount = picker.form.querySelector('span.comment-character-count');
if (this.ui.emojiPicker) {
this.ui.emojiPickerUI = this.ui.emojiPicker.querySelector('.comment-emoji-picker');
this.ui.picmo = picmo.createPicker({
picker.picmo = picmo.createPicker({
emojisPerRow: 7,
rootElement: this.ui.emojiPickerUI,
rootElement: picker.ui,
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);
UIkit.util.on(this.ui.emojiPicker, 'show', ( ) => {
picker.emojiPickerDrop = UIkit.drop(picker.drop);
UIkit.util.on(picker.drop, 'show', ( ) => {
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) {
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) {
@ -177,6 +195,7 @@ export default class SiteComments {
try {
const response = await fetch(`/comment/${commentId}/replies`);
this.app.processResponse(response);
this.createEmojiPickers();
} catch (error) {
UIkit.modal.alert(`Failed to load replies: ${error.message}`);
}
@ -209,36 +228,38 @@ export default class SiteComments {
try {
const response = await fetch(`${rootUrl}?p=${nextPage}&buttonId=${buttonId}`);
await this.app.processResponse(response);
this.createEmojiPickers();
} catch (error) {
UIkit.modal.alert(`Failed to load more comments: ${error.message}`);
}
}
async onEmojiSelected (event) {
this.ui.emojiPickerDrop.hide(false);
return this.insertContentAtCursor(event.emoji);
async onEmojiSelected (picker, event) {
picker.emojiPickerDrop.hide(false);
await this.insertContentAtCursor(picker, event.emoji);
picker.characterCount.textContent = numeral(picker.input.value.charCount()).format('0,0');
}
async insertContentAtCursor (content) {
this.ui.input.focus();
async insertContentAtCursor (picker, content) {
picker.input.focus();
if (document.selection) {
let sel = document.selection.createRange();
sel.text = content;
} else if (this.ui.input.selectionStart || (this.ui.input.selectionStart === 0)) {
let startPos = this.ui.input.selectionStart;
let endPos = this.ui.input.selectionEnd;
} else if (picker.input.selectionStart || (picker.input.selectionStart === 0)) {
let startPos = picker.input.selectionStart;
let endPos = picker.input.selectionEnd;
let oldLength = this.ui.input.value.length;
this.ui.input.value =
this.ui.input.value.substring(0, startPos) +
let oldLength = picker.input.value.length;
picker.input.value =
picker.input.value.substring(0, startPos) +
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);
this.ui.input.selectionEnd = this.ui.input.selectionStart;
picker.input.selectionStart = startPos + (picker.input.value.length - oldLength);
picker.input.selectionEnd = picker.input.selectionStart;
} else {
this.ui.input.value += content;
picker.input.value += content;
}
}
}
Loading…
Cancel
Save