forked from nostr/nostrweb
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
parent
b10a63b932
commit
06686ba9a7
30
src/form.css
30
src/form.css
|
@ -28,7 +28,8 @@ label {
|
|||
}
|
||||
|
||||
input[type="password"],
|
||||
input[type="text"] {
|
||||
input[type="text"],
|
||||
textarea {
|
||||
background: var(--bgcolor-textinput);
|
||||
border: .2rem solid #b7b7b7;
|
||||
border-radius: .2rem;
|
||||
|
@ -36,10 +37,21 @@ input[type="text"] {
|
|||
margin: 0;
|
||||
}
|
||||
input[type="password"]:focus,
|
||||
input[type="text"]:focus {
|
||||
input[type="text"]:focus,
|
||||
textarea:focus {
|
||||
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 {
|
||||
align-items: center;
|
||||
|
@ -108,19 +120,25 @@ button:disabled {
|
|||
gap: 1rem;
|
||||
}
|
||||
.cards .form-inline button,
|
||||
.cards .form-inline input[type="text"] {
|
||||
.cards .form-inline input[type="text"],
|
||||
.cards .form-inline textarea {
|
||||
margin: .4rem 0;
|
||||
padding: .6rem 1rem;
|
||||
}
|
||||
|
||||
.form-inline input[type="text"] {
|
||||
.form-inline input[type="text"],
|
||||
.form-inline textarea {
|
||||
flex-grow: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-inline button {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.form-inline button#publish {
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
.focus-active {
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<img class="mbox-img" id="bubble" src="assets/comment.svg" alt="">
|
||||
<div class="mbox-body" id="newMessage">
|
||||
<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>
|
||||
</form>
|
||||
<small id="sendstatus" class="form-status"></small>
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
/* 5px auto Highlight */
|
||||
--focus-border-color: rgb(0, 122, 255);
|
||||
--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-width: 2px;
|
||||
--focus-outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
|
||||
|
|
67
src/main.js
67
src/main.js
|
@ -120,7 +120,8 @@ function handleReaction(evt, relay) {
|
|||
// feed
|
||||
const feedContainer = document.querySelector('#homefeed');
|
||||
const feedDomMap = {};
|
||||
const replyDomMap = window.replyDomMap = {};
|
||||
const replyDomMap = {};
|
||||
const restoredReplyTo = localStorage.getItem('reply_to');
|
||||
|
||||
const sortByCreatedAt = (evt1, evt2) => {
|
||||
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'})]),
|
||||
replies[0] ? elem('div', {className: 'mobx-replies'}, replyFeed.reverse()) : '',
|
||||
]);
|
||||
if (restoredReplyTo === evt.id) appendReplyForm(body.querySelector('button[name="reply"]'));
|
||||
return rendernArticle([img, body]);
|
||||
}
|
||||
|
||||
|
@ -359,23 +361,11 @@ function getMetadata(evt, relay) {
|
|||
}
|
||||
|
||||
// reply
|
||||
const writeForm = document.querySelector('#writeForm');
|
||||
const input = document.querySelector('textarea[name="message"]');
|
||||
let lastReplyBtn = null;
|
||||
let replyTo = null;
|
||||
feedContainer.addEventListener('click', (e) => {
|
||||
const button = e.target.closest('button');
|
||||
if (button && button.name === 'reply') {
|
||||
if (lastReplyBtn) {
|
||||
lastReplyBtn.hidden = false;
|
||||
}
|
||||
lastReplyBtn = button;
|
||||
// button.hidden = true;
|
||||
button.after(writeForm);
|
||||
button.after(sendStatus);
|
||||
writeForm.hidden = false;
|
||||
replyTo = ['e', button.dataset.eventId, button.dataset.relay];
|
||||
input.focus();
|
||||
appendReplyForm(button);
|
||||
localStorage.setItem('reply_to', button.dataset.eventId);
|
||||
return;
|
||||
}
|
||||
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');
|
||||
document.querySelector('#bubble').addEventListener('click', (e) => {
|
||||
replyTo = null;
|
||||
localStorage.removeItem('reply_to');
|
||||
newMessageDiv.prepend(writeForm);
|
||||
newMessageDiv.append(sendStatus);
|
||||
input.focus();
|
||||
writeInput.focus();
|
||||
});
|
||||
|
||||
async function upvote(eventId, relay) {
|
||||
|
@ -428,14 +427,15 @@ writeForm.addEventListener('submit', async (e) => {
|
|||
if (!pubkey || !privatekey) {
|
||||
return onSendError(new Error('no pubkey/privatekey'));
|
||||
}
|
||||
if (!input.value) {
|
||||
if (!writeInput.value) {
|
||||
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 = {
|
||||
kind: 1,
|
||||
pubkey,
|
||||
content: input.value,
|
||||
content: writeInput.value,
|
||||
tags,
|
||||
created_at: Math.floor(Date.now() * 0.001),
|
||||
};
|
||||
|
@ -447,12 +447,11 @@ writeForm.addEventListener('submit', async (e) => {
|
|||
}
|
||||
if (status === 1) {
|
||||
sendStatus.hidden = true;
|
||||
input.value = '';
|
||||
writeInput.value = '';
|
||||
writeInput.style.removeProperty('height');
|
||||
publish.disabled = true;
|
||||
if (lastReplyBtn) {
|
||||
lastReplyBtn.hidden = false;
|
||||
lastReplyBtn = null;
|
||||
replyTo = null;
|
||||
if (replyTo) {
|
||||
localStorage.removeItem('reply_to');
|
||||
newMessageDiv.append(writeForm);
|
||||
newMessageDiv.append(sendStatus);
|
||||
}
|
||||
|
@ -462,8 +461,22 @@ writeForm.addEventListener('submit', async (e) => {
|
|||
}
|
||||
});
|
||||
|
||||
input.addEventListener('input', () => publish.disabled = !input.value);
|
||||
input.addEventListener('blur', () => sendStatus.textContent = '');
|
||||
writeInput.addEventListener('input', () => {
|
||||
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
|
||||
const settingsForm = document.querySelector('form[name="settings"]');
|
||||
|
|
Loading…
Reference in New Issue