post: improve write and reply to textnote usability

- use growing textarea input to support multiline textnotes
- keep replyTo id in localstorage instead of global variable
- render reply to form on load if there was a replyTo in storage
pull/13/head
OFF0 2 years ago
parent b10a63b932
commit 06686ba9a7
Signed by: offbyn
GPG Key ID: 94A2F643C51F37FA

@ -28,7 +28,8 @@ label {
} }
input[type="password"], input[type="password"],
input[type="text"] { input[type="text"],
textarea {
background: var(--bgcolor-textinput); background: var(--bgcolor-textinput);
border: .2rem solid #b7b7b7; border: .2rem solid #b7b7b7;
border-radius: .2rem; border-radius: .2rem;
@ -36,11 +37,22 @@ input[type="text"] {
margin: 0; margin: 0;
} }
input[type="password"]:focus, input[type="password"]:focus,
input[type="text"]:focus { input[type="text"]:focus,
textarea:focus {
border-color: var(--focus-border-color); border-color: var(--focus-border-color);
outline-offset: 2px; outline-offset: var(--focus-outline-offset);
outline: var(--focus-outline);
}
textarea {
max-height: 50vh;
min-height: 20px;
transition: min-height .1s ease-out, height .1s ease-out;
}
textarea:focus {
min-height: 3.5rem;
} }
.buttons { .buttons {
align-items: center; align-items: center;
display: flex; display: flex;
@ -108,19 +120,25 @@ button:disabled {
gap: 1rem; gap: 1rem;
} }
.cards .form-inline button, .cards .form-inline button,
.cards .form-inline input[type="text"] { .cards .form-inline input[type="text"],
.cards .form-inline textarea {
margin: .4rem 0; margin: .4rem 0;
padding: .6rem 1rem;
} }
.form-inline input[type="text"] { .form-inline input[type="text"],
.form-inline textarea {
flex-grow: 1; flex-grow: 1;
margin-bottom: 0;
} }
.form-inline button { .form-inline button {
flex-grow: 0; flex-grow: 0;
} }
.form-inline button#publish {
align-self: end;
}
.focus-active { .focus-active {
} }

@ -25,7 +25,7 @@
<img class="mbox-img" id="bubble" src="assets/comment.svg" alt=""> <img class="mbox-img" id="bubble" src="assets/comment.svg" alt="">
<div class="mbox-body" id="newMessage"> <div class="mbox-body" id="newMessage">
<form action="#" class="form-inline" id="writeForm"> <form action="#" class="form-inline" id="writeForm">
<textarea name="message"></textarea> <textarea name="message" rows="1"></textarea>
<button type="submit" id="publish" disabled>send</button> <button type="submit" id="publish" disabled>send</button>
</form> </form>
<small id="sendstatus" class="form-status"></small> <small id="sendstatus" class="form-status"></small>

@ -6,7 +6,8 @@
/* 5px auto Highlight */ /* 5px auto Highlight */
--focus-border-color: rgb(0, 122, 255); --focus-border-color: rgb(0, 122, 255);
--focus-border-radius: 2px; --focus-border-radius: 2px;
--focus-outline-color: rgb(127, 189, 247); --focus-outline-color: rgb(192, 227, 252);
--focus-outline-offset: 2px;
--focus-outline-style: solid; --focus-outline-style: solid;
--focus-outline-width: 2px; --focus-outline-width: 2px;
--focus-outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color); --focus-outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);

@ -120,7 +120,8 @@ function handleReaction(evt, relay) {
// feed // feed
const feedContainer = document.querySelector('#homefeed'); const feedContainer = document.querySelector('#homefeed');
const feedDomMap = {}; const feedDomMap = {};
const replyDomMap = window.replyDomMap = {}; const replyDomMap = {};
const restoredReplyTo = localStorage.getItem('reply_to');
const sortByCreatedAt = (evt1, evt2) => { const sortByCreatedAt = (evt1, evt2) => {
if (evt1.created_at === evt2.created_at) { if (evt1.created_at === evt2.created_at) {
@ -191,6 +192,7 @@ function createTextNote(evt, relay) {
}, [elem('img', {height: 24, width: 24, src: 'assets/comment.svg'})]), }, [elem('img', {height: 24, width: 24, src: 'assets/comment.svg'})]),
replies[0] ? elem('div', {className: 'mobx-replies'}, replyFeed.reverse()) : '', replies[0] ? elem('div', {className: 'mobx-replies'}, replyFeed.reverse()) : '',
]); ]);
if (restoredReplyTo === evt.id) appendReplyForm(body.querySelector('button[name="reply"]'));
return rendernArticle([img, body]); return rendernArticle([img, body]);
} }
@ -359,23 +361,11 @@ function getMetadata(evt, relay) {
} }
// reply // reply
const writeForm = document.querySelector('#writeForm');
const input = document.querySelector('textarea[name="message"]');
let lastReplyBtn = null;
let replyTo = null;
feedContainer.addEventListener('click', (e) => { feedContainer.addEventListener('click', (e) => {
const button = e.target.closest('button'); const button = e.target.closest('button');
if (button && button.name === 'reply') { if (button && button.name === 'reply') {
if (lastReplyBtn) { appendReplyForm(button);
lastReplyBtn.hidden = false; localStorage.setItem('reply_to', button.dataset.eventId);
}
lastReplyBtn = button;
// button.hidden = true;
button.after(writeForm);
button.after(sendStatus);
writeForm.hidden = false;
replyTo = ['e', button.dataset.eventId, button.dataset.relay];
input.focus();
return; return;
} }
if (button && button.name === 'star') { if (button && button.name === 'star') {
@ -384,12 +374,21 @@ feedContainer.addEventListener('click', (e) => {
} }
}); });
const writeForm = document.querySelector('#writeForm');
const writeInput = document.querySelector('textarea[name="message"]');
function appendReplyForm(el) {
el.after(writeForm);
el.after(sendStatus);
writeInput.focus();
}
const newMessageDiv = document.querySelector('#newMessage'); const newMessageDiv = document.querySelector('#newMessage');
document.querySelector('#bubble').addEventListener('click', (e) => { document.querySelector('#bubble').addEventListener('click', (e) => {
replyTo = null; localStorage.removeItem('reply_to');
newMessageDiv.prepend(writeForm); newMessageDiv.prepend(writeForm);
newMessageDiv.append(sendStatus); newMessageDiv.append(sendStatus);
input.focus(); writeInput.focus();
}); });
async function upvote(eventId, relay) { async function upvote(eventId, relay) {
@ -428,14 +427,15 @@ writeForm.addEventListener('submit', async (e) => {
if (!pubkey || !privatekey) { if (!pubkey || !privatekey) {
return onSendError(new Error('no pubkey/privatekey')); return onSendError(new Error('no pubkey/privatekey'));
} }
if (!input.value) { if (!writeInput.value) {
return onSendError(new Error('message is empty')); return onSendError(new Error('message is empty'));
} }
const tags = replyTo ? [replyTo] : []; const replyTo = localStorage.getItem('reply_to');
const tags = replyTo ? [['e', replyTo, eventRelayMap[replyTo][0]]] : [];
const newEvent = { const newEvent = {
kind: 1, kind: 1,
pubkey, pubkey,
content: input.value, content: writeInput.value,
tags, tags,
created_at: Math.floor(Date.now() * 0.001), created_at: Math.floor(Date.now() * 0.001),
}; };
@ -447,12 +447,11 @@ writeForm.addEventListener('submit', async (e) => {
} }
if (status === 1) { if (status === 1) {
sendStatus.hidden = true; sendStatus.hidden = true;
input.value = ''; writeInput.value = '';
writeInput.style.removeProperty('height');
publish.disabled = true; publish.disabled = true;
if (lastReplyBtn) { if (replyTo) {
lastReplyBtn.hidden = false; localStorage.removeItem('reply_to');
lastReplyBtn = null;
replyTo = null;
newMessageDiv.append(writeForm); newMessageDiv.append(writeForm);
newMessageDiv.append(sendStatus); newMessageDiv.append(sendStatus);
} }
@ -462,8 +461,22 @@ writeForm.addEventListener('submit', async (e) => {
} }
}); });
input.addEventListener('input', () => publish.disabled = !input.value); writeInput.addEventListener('input', () => {
input.addEventListener('blur', () => sendStatus.textContent = ''); publish.disabled = !writeInput.value;
updateElemHeight(writeInput);
});
writeInput.addEventListener('blur', () => sendStatus.textContent = '');
function updateElemHeight(el) {
el.style.removeProperty('height');
if (el.value) {
el.style.paddingBottom = 0;
el.style.paddingTop = 0;
el.style.height = el.scrollHeight + 'px';
el.style.removeProperty('padding-bottom');
el.style.removeProperty('padding-top');
}
}
// settings // settings
const settingsForm = document.querySelector('form[name="settings"]'); const settingsForm = document.querySelector('form[name="settings"]');

Loading…
Cancel
Save