|
/**
|
|
* @fileoverview Post Form Component for Rantii
|
|
* @author retoor <retoor@molodetz.nl>
|
|
* @description Form for creating new rants
|
|
* @keywords post, form, create, rant, new
|
|
*/
|
|
|
|
import { BaseComponent } from './base-component.js';
|
|
|
|
class PostForm extends BaseComponent {
|
|
init() {
|
|
this.isSubmitting = false;
|
|
this.render();
|
|
this.bindEvents();
|
|
this.loadDraft();
|
|
}
|
|
|
|
render() {
|
|
const isLoggedIn = this.isLoggedIn();
|
|
|
|
this.addClass('post-form');
|
|
|
|
if (!isLoggedIn) {
|
|
this.setHtml(`
|
|
<div class="post-form-auth">
|
|
<p>Sign in to post</p>
|
|
<button class="btn btn-primary login-btn">Sign In</button>
|
|
</div>
|
|
`);
|
|
return;
|
|
}
|
|
|
|
this.setHtml(`
|
|
<form class="post-form-inner">
|
|
<header class="form-header">
|
|
<h2>Create Rant</h2>
|
|
<button type="button" class="close-btn" aria-label="Close">
|
|
<svg viewBox="0 0 24 24" width="24" height="24">
|
|
<path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
|
</svg>
|
|
</button>
|
|
</header>
|
|
<div class="form-group">
|
|
<textarea
|
|
class="post-input"
|
|
placeholder="What's on your mind?"
|
|
rows="6"
|
|
maxlength="5000"
|
|
${this.isSubmitting ? 'disabled' : ''}></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<input
|
|
type="text"
|
|
class="tags-input"
|
|
placeholder="Tags (comma separated)"
|
|
maxlength="100"
|
|
${this.isSubmitting ? 'disabled' : ''}>
|
|
</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');
|
|
const closeBtn = e.target.closest('.close-btn');
|
|
|
|
if (loginBtn) {
|
|
this.getRouter()?.goToLogin();
|
|
return;
|
|
}
|
|
|
|
if (closeBtn) {
|
|
this.emit('close');
|
|
}
|
|
}
|
|
|
|
async handleSubmit(e) {
|
|
e.preventDefault();
|
|
|
|
if (this.isSubmitting) return;
|
|
|
|
const textarea = this.$('.post-input');
|
|
const tagsInput = this.$('.tags-input');
|
|
|
|
if (!textarea) return;
|
|
|
|
const text = textarea.value.trim();
|
|
if (!text) {
|
|
this.getApp()?.toast?.error('Please enter some text');
|
|
return;
|
|
}
|
|
|
|
const tags = tagsInput ? tagsInput.value.trim() : '';
|
|
|
|
this.isSubmitting = true;
|
|
this.render();
|
|
|
|
try {
|
|
const result = await this.getApi()?.postRant(text, tags);
|
|
if (result?.success) {
|
|
this.clearDraft();
|
|
this.emit('post-created', { rantId: result.rantId });
|
|
this.getApp()?.toast?.success('Rant posted successfully');
|
|
this.isSubmitting = false;
|
|
this.render();
|
|
this.getRouter()?.goToRant(result.rantId);
|
|
} else {
|
|
this.getApp()?.toast?.error(result?.error || 'Failed to post');
|
|
this.isSubmitting = false;
|
|
this.render();
|
|
}
|
|
} catch (error) {
|
|
this.getApp()?.toast?.error('Failed to post');
|
|
this.isSubmitting = false;
|
|
this.render();
|
|
}
|
|
}
|
|
|
|
handleInput(e) {
|
|
if (e.target.classList.contains('post-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 draft = this.getStorage()?.getDraftRant();
|
|
if (draft) {
|
|
const textarea = this.$('.post-input');
|
|
if (textarea) {
|
|
textarea.value = draft;
|
|
const countEl = this.$('.char-count');
|
|
if (countEl) {
|
|
countEl.textContent = `${draft.length} / 5000`;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
saveDraft(text) {
|
|
this.getStorage()?.setDraftRant(text);
|
|
}
|
|
|
|
clearDraft() {
|
|
this.getStorage()?.clearDraftRant();
|
|
}
|
|
|
|
reset() {
|
|
const textarea = this.$('.post-input');
|
|
if (textarea) {
|
|
textarea.value = '';
|
|
}
|
|
const tagsInput = this.$('.tags-input');
|
|
if (tagsInput) {
|
|
tagsInput.value = '';
|
|
}
|
|
const countEl = this.$('.char-count');
|
|
if (countEl) {
|
|
countEl.textContent = '0 / 5000';
|
|
}
|
|
this.clearDraft();
|
|
}
|
|
|
|
focus() {
|
|
const textarea = this.$('.post-input');
|
|
if (textarea) {
|
|
textarea.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define('post-form', PostForm);
|
|
|
|
class PostModal extends BaseComponent {
|
|
init() {
|
|
this.keydownHandler = (e) => this.handleKeydown(e);
|
|
this.render();
|
|
this.bindEvents();
|
|
}
|
|
|
|
render() {
|
|
this.addClass('modal', 'post-modal');
|
|
|
|
this.setHtml(`
|
|
<div class="modal-backdrop"></div>
|
|
<div class="modal-content">
|
|
<post-form></post-form>
|
|
</div>
|
|
`);
|
|
|
|
requestAnimationFrame(() => this.addClass('modal-visible'));
|
|
}
|
|
|
|
bindEvents() {
|
|
this.on(this, 'click', this.handleClick);
|
|
this.on(this, 'close', this.close);
|
|
this.on(this, 'post-created', this.close);
|
|
document.addEventListener('keydown', this.keydownHandler);
|
|
}
|
|
|
|
handleClick(e) {
|
|
if (e.target.classList.contains('modal-backdrop')) {
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
handleKeydown(e) {
|
|
if (e.key === 'Escape') {
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
close() {
|
|
document.removeEventListener('keydown', this.keydownHandler);
|
|
this.removeClass('modal-visible');
|
|
setTimeout(() => this.remove(), 300);
|
|
}
|
|
|
|
open() {
|
|
document.body.appendChild(this);
|
|
const form = this.$('post-form');
|
|
if (form) {
|
|
form.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define('post-modal', PostModal);
|
|
|
|
export { PostForm, PostModal };
|