181 lines
5.0 KiB
JavaScript
181 lines
5.0 KiB
JavaScript
|
|
/**
|
||
|
|
* @fileoverview Comment Form Component for Rantii
|
||
|
|
* @author retoor <retoor@molodetz.nl>
|
||
|
|
* @description Form for posting new comments
|
||
|
|
* @keywords comment, form, post, reply, input
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { BaseComponent } from './base-component.js';
|
||
|
|
|
||
|
|
class CommentForm extends BaseComponent {
|
||
|
|
static get observedAttributes() {
|
||
|
|
return ['rant-id'];
|
||
|
|
}
|
||
|
|
|
||
|
|
init() {
|
||
|
|
this.isSubmitting = false;
|
||
|
|
this.render();
|
||
|
|
this.bindEvents();
|
||
|
|
this.loadDraft();
|
||
|
|
}
|
||
|
|
|
||
|
|
render() {
|
||
|
|
const rantId = this.getAttr('rant-id');
|
||
|
|
const isLoggedIn = this.isLoggedIn();
|
||
|
|
|
||
|
|
this.addClass('comment-form');
|
||
|
|
|
||
|
|
if (!isLoggedIn) {
|
||
|
|
this.setHtml(`
|
||
|
|
<div class="comment-form-auth">
|
||
|
|
<p>Sign in to comment</p>
|
||
|
|
<button class="btn btn-primary login-btn">Sign In</button>
|
||
|
|
</div>
|
||
|
|
`);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.setHtml(`
|
||
|
|
<form class="comment-form-inner">
|
||
|
|
<div class="form-group">
|
||
|
|
<textarea
|
||
|
|
class="comment-input"
|
||
|
|
placeholder="Write a comment..."
|
||
|
|
rows="3"
|
||
|
|
maxlength="5000"
|
||
|
|
${this.isSubmitting ? 'disabled' : ''}></textarea>
|
||
|
|
</div>
|
||
|
|
<div class="form-actions">
|
||
|
|
<span class="char-count">0 / 5000</span>
|
||
|
|
<button type="submit"
|
||
|
|
class="btn btn-primary submit-btn"
|
||
|
|
${this.isSubmitting ? 'disabled' : ''}>
|
||
|
|
${this.isSubmitting ? '<loading-spinner size="small"></loading-spinner>' : 'Post'}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
`);
|
||
|
|
}
|
||
|
|
|
||
|
|
bindEvents() {
|
||
|
|
this.on(this, 'click', this.handleClick);
|
||
|
|
this.on(this, 'submit', this.handleSubmit);
|
||
|
|
this.on(this, 'input', this.handleInput);
|
||
|
|
}
|
||
|
|
|
||
|
|
handleClick(e) {
|
||
|
|
const loginBtn = e.target.closest('.login-btn');
|
||
|
|
if (loginBtn) {
|
||
|
|
this.getRouter()?.goToLogin();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async handleSubmit(e) {
|
||
|
|
e.preventDefault();
|
||
|
|
|
||
|
|
if (this.isSubmitting) return;
|
||
|
|
|
||
|
|
const textarea = this.$('.comment-input');
|
||
|
|
if (!textarea) return;
|
||
|
|
|
||
|
|
const text = textarea.value.trim();
|
||
|
|
if (!text) return;
|
||
|
|
|
||
|
|
const rantId = this.getAttr('rant-id');
|
||
|
|
if (!rantId) return;
|
||
|
|
|
||
|
|
this.isSubmitting = true;
|
||
|
|
this.render();
|
||
|
|
|
||
|
|
try {
|
||
|
|
const result = await this.getApi()?.postComment(rantId, text);
|
||
|
|
if (result?.success) {
|
||
|
|
this.clearDraft();
|
||
|
|
this.emit('comment-posted', { rantId, comment: result.comment });
|
||
|
|
this.isSubmitting = false;
|
||
|
|
this.render();
|
||
|
|
} else {
|
||
|
|
this.getApp()?.toast?.error(result?.error || 'Failed to post comment');
|
||
|
|
this.isSubmitting = false;
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
this.getApp()?.toast?.error('Failed to post comment');
|
||
|
|
this.isSubmitting = false;
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
handleInput(e) {
|
||
|
|
if (e.target.classList.contains('comment-input')) {
|
||
|
|
const textarea = e.target;
|
||
|
|
const count = textarea.value.length;
|
||
|
|
const countEl = this.$('.char-count');
|
||
|
|
if (countEl) {
|
||
|
|
countEl.textContent = `${count} / 5000`;
|
||
|
|
}
|
||
|
|
this.saveDraft(textarea.value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
loadDraft() {
|
||
|
|
const rantId = this.getAttr('rant-id');
|
||
|
|
if (rantId) {
|
||
|
|
const draft = this.getStorage()?.getDraftComment(rantId);
|
||
|
|
if (draft) {
|
||
|
|
const textarea = this.$('.comment-input');
|
||
|
|
if (textarea) {
|
||
|
|
textarea.value = draft;
|
||
|
|
const countEl = this.$('.char-count');
|
||
|
|
if (countEl) {
|
||
|
|
countEl.textContent = `${draft.length} / 5000`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
saveDraft(text) {
|
||
|
|
const rantId = this.getAttr('rant-id');
|
||
|
|
if (rantId) {
|
||
|
|
this.getStorage()?.setDraftComment(rantId, text);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
clearDraft() {
|
||
|
|
const rantId = this.getAttr('rant-id');
|
||
|
|
if (rantId) {
|
||
|
|
this.getStorage()?.clearDraftComment(rantId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
reset() {
|
||
|
|
const textarea = this.$('.comment-input');
|
||
|
|
if (textarea) {
|
||
|
|
textarea.value = '';
|
||
|
|
}
|
||
|
|
const countEl = this.$('.char-count');
|
||
|
|
if (countEl) {
|
||
|
|
countEl.textContent = '0 / 5000';
|
||
|
|
}
|
||
|
|
this.clearDraft();
|
||
|
|
}
|
||
|
|
|
||
|
|
focus() {
|
||
|
|
const textarea = this.$('.comment-input');
|
||
|
|
if (textarea) {
|
||
|
|
textarea.focus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onAttributeChanged(name, oldValue, newValue) {
|
||
|
|
if (name === 'rant-id') {
|
||
|
|
this.loadDraft();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
customElements.define('comment-form', CommentForm);
|
||
|
|
|
||
|
|
export { CommentForm };
|