Скрипт для відправки дописів у Steemit з редактором HTML-тегів
Ви напевно чули, що рухає прогрес. Лінь. І тільки лінь. Мені набридло вручну вводити HTML-теги в тексти дописів. Я довго думав, як вирішити це питання. І не тому що я повільно думаю, а часто просто не вистачає часу на розробку чогось, у житті пріоритети розставляються не завжди так як нам хочеться. Останніми днями мені вдалося приділити увагу багатьом справам, які я відклав у далекий ящик. І я кажу не тільки про Steemit.
Кілька місяців тому я писав у спільноті Steem Dev невеликий допис з пропозицією додати кнопки до редактора тексту в режимі розмітки (markdown), за допомогою яких можна було б легко додавати HTML-теги. Я нікого не зацікавив і це нормально, я знаю що на платформі Steemit є багато інших важливих задач, які потребують фундаментального підходу.
Створити сторінку для публікації допису виявилося не дуже складною справою. Два дні я намагався розібратися з механізмом завантаження зображень на сервер steemitimages, але так нічого й не вийшло. Справа в тому, що запит на завантаження картинки відбувається з токеном із cookie, який видається під час авторизації на сайті steemit.com, таким чином завантаження картинки через інший домен стає неможливим. Гарним рішенням мені могло би стати браузерне розширення SteemKeychain, але я свідомо використовую приватний постинг-ключ, щоб використовувати цей скрипт на мобільному телефоні, попри те що це найнебезпечніший спосіб авторизації. Теорія з токеном із cookie не стовідсоткова, але має право на існування. Таким чином я залишаю за собою право бути неправим :)На мій погляд, html-розмітка краща й зручніша, ніж візуальний редактор. Спробую пояснити чому. Оформлюючи текст у HTML, ти точно знаєш де що знаходиться, і не залежиш від обмежень візуального редактора. Наприклад, ти можеш задати обтікання картинки текстом так як тобі потрібно, а не так як передбачено в редакторі. Є можливість гнучкого налаштування параметрів. Ви не використовуєте нічого зайвого. У той час як візуальний редактор дуже часто дублює теги, залишає в коді багато "сміття". Якщо під час оформлення щось "з'їхало", то в коді одразу видно чому. Коли хочеться створити щось своє, а не те що дозволяє візуальний редактор, то HTML завжди у виграші.
Якщо ви маєте намір експериментувати з роботою скрипта для відправки дописів у Steemit, хочу звернути вашу увагу на один момент. Новий допис визначається парою параметрів: author і permlink. Якщо ви надсилаєте той самий permlink (заголовок допису), то блокчейн не створює новий допис, а відредагує вже існуючий із ідентичним permlink, змінить лише last_update.
Скрипт нижче, який я хочу продемонструвати, дозволяє вставляти в текстове поле основні теги, які підтримуються в Steemit, а також код посилання й готові блочні елементи:
<div class="pull-left"></div>
- HTML-блок з класом pull-left, який вирівнює вміст по лівому краю.
<div class="pull-right"></div>
- HTML-блок з класом pull-left, який вирівнює вміст по правому краю.
<div class="text-rtl"></div>
- HTML-блок з класом text-rtl, який задає напрямок тексту справа наліво.
<div class="text-justify"></div>
- HTML-блок з класом text-justify, який вирівнює текст по ширині.
Під час використання тегів DIV при оформленні дописів у Steemit я дуже рекомендую обгортати їх у тег P, інакше втрачається відступ між абзацом із DIV і наступним абзацом. Наприклад, замість <div>текст</div>
використовуйте <p><div>текст</div></p>
.
Приклад використання HTML-блока з класом pull-left і тега P:
Теги ul і li немає сенсу вписувати в код вручну, оскільки символ "зірочка" (*) повністю їх замінює.
Отже, розберемо устрій скрипта за основними важливими сегментами:
<label for="author">Username:</label>
<input type="text" id="author" placeholder="Username" value="" required>
<label for="key">Private posting key:</label>
<input type="password" id="key" placeholder="Private posting key" value="" required>
<label for="title">Title:</label>
<input type="text" id="title" placeholder="Title" value="" required>
<label for="tags">Tags (space separated):</label>
<input type="text" id="tags" placeholder="e.g. travel ukraine photo" value="" required>
<label for="body">Post body:</label>
<div class="formTxt">
<input type="button" class="format strong" title="Bold" onclick="tag('strong')" value="B" />
<input type="button" class="format em" title="Italics" onclick="tag('em')" value="I" />
<input type="button" class="format del" title="Strike" onclick="tag('strike')" value="S" />
<input type="button" class="format" title="Insert URL" onclick="tag('link')" value="URL" />
<input type="button" class="format" title="P" onclick="tag('p')" value="P" />
<input type="button" class="format" title="DIV" onclick="tag('div')" value="DIV" />
<input type="button" class="format" title="Text direction right to left" onclick="tag('text-rtl')" value="Text RTL" />
<input type="button" class="format" title="Align Right" onclick="tag('align-right')" value="Align DIV Right" />
<input type="button" class="format" title="Align Left" onclick="tag('align-left')" value="Align DIV Left" />
<input type="button" class="format" title="Text Justify" onclick="tag('text-justify')" value="☰" />
</div>
<textarea id="body" placeholder="Post body"></textarea>
Введення особистих даних, заголовка майбутнього допису, тегів і тексту. Над текстовим полем (textarea) розташовані кнопки для додавання тегів і блочних елементів. Наприклад, щоб додати тег strong, потрібно виділити необхідне слово або шматок тексту й натиснути на кнопку. Це дуже зручно.
const comment = {
parent_author: '',
parent_permlink: parentPermlink,
author,
permlink,
title,
body,
json_metadata: JSON.stringify({
tags: tags,
app: 'script/0.1',
format: 'markdown'
})
};
Тут визначається структура створюваного допису. Систематизуємо дані, визначаємо автора, заголовок і т.д. Зверніть увагу на рядок app: 'script/0.1'
— все, що опубліковано через сайт Steemit, підписується як steemit/0.2, тобто ви можете замінити цей текст на що завгодно, навіть на ім’я свого кота.
Відправлення допису відбувається при натисканні на кнопку <button onclick="sendPost()">Publish</button>
, після чого викликається функція відправки sendPost()
. Результат цієї дії буде відображено в блоці <div class="log" id="log"></div>
, це своєрідний звіт про відправку.
Функція function tag(tag) дозволяє вставляти HTML-теги в текст допису при натисканні на відповідні кнопки (описано раніше). Скрипт "бачить", куди вставляти теги за допомогою id="body"
, у моєму прикладі він прописаний у textarea.
Не забудьте, що підключення до Steemit через API можливе лише після підключення бібліотеки dsteem. Посилання на бібліотеку dsteem вказане в шапці сайту:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/dsteem.min.js"></script>
Якщо вказане посилання недоступне, просто завантажте цю бібліотеку (можливо, доведеться змінити розширення файлу *.JPG на *.JS).
Увесь код:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Steemit Post</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/dsteem.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
font: 16px/1.4 arial, sans-serif;
margin: 24px;
max-width: 800px;
margin-inline: auto;
}
#main {
margin: 0 auto;
width: 800px;
}
label {
display: block;
margin: .5rem 0 .25rem;
}
input[type="text"],
input[type="password"],
textarea {
width: 100%;
padding: .6rem;
font-size: 16px;
border: 1px solid #b3c1c2;
border-radius: 4px;
}
textarea {
height: 150px;
}
button {
margin-top: 1rem;
padding: .7rem 1rem;
cursor: pointer;
font-size: 16px;
width: auto;
border: 1px solid #b3c1c2;
border-radius: 4px;
}
.log {
margin-top: 1rem;
white-space: pre-wrap;
padding: 1rem;
}
/* Formatting panel */
.formTxt {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin: 6px 0 10px;
}
/* Formatting buttons */
input.format {
border: 1px solid #b3c1c2;
color: #000;
font-size: .95em;
line-height: 1.5em;
cursor: pointer;
background: #f9f6f2;
padding: .15em .7em;
border-radius: 4px;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 32px;
min-width: 34px;
width: auto;
margin-bottom: 2px;
}
.strong {
font-weight: bold;
}
.em {
font-style: italic;
}
.underline {
text-decoration: underline;
}
.del {
text-decoration: line-through;
}
/* Mobile adaptation */
@media (max-width: 600px) {
body {
font-size: 15px;
}
#main {
margin: 0 auto;
width: 95%;
}
input[type="text"],
input[type="password"],
textarea,
button {
font-size: 16px;
width: 90%;
}
textarea {
height: 140px;
}
.formTxt {
gap: 3px;
flex-wrap: wrap;
}
input.format {
font-size: .93em;
padding: .18em .35em;
min-width: 30px;
min-height: 28px;
width: auto;
}
button {
width: 100%;
}
}
</style>
</head>
<body>
<div id="main">
<h1>Publish to Steemit</h1>
<label for="author">Username:</label>
<input type="text" id="author" placeholder="Steemit Username" value="" required>
<label for="key">Private posting key:</label>
<input type="password" id="key" placeholder="Private posting key" value="" required>
<label for="title">Title:</label>
<input type="text" id="title" placeholder="Title" value="" required>
<label for="tags">Tags (space separated):</label>
<input type="text" id="tags" placeholder="e.g. travel ukraine photo" value="" required>
<label for="body">Post body:</label>
<div class="formTxt">
<input type="button" class="format strong" title="Bold" onclick="tag('strong')" value="B" />
<input type="button" class="format em" title="Italics" onclick="tag('em')" value="I" />
<input type="button" class="format del" title="Strike" onclick="tag('strike')" value="S" />
<input type="button" class="format" title="Insert URL" onclick="tag('link')" value="URL" />
<input type="button" class="format" title="P" onclick="tag('p')" value="P" />
<input type="button" class="format" title="DIV" onclick="tag('div')" value="DIV" />
<input type="button" class="format" title="Text direction right to left" onclick="tag('text-rtl')" value="Text RTL" />
<input type="button" class="format" title="Align Right" onclick="tag('align-right')" value="Align DIV Right" />
<input type="button" class="format" title="Align Left" onclick="tag('align-left')" value="Align DIV Left" />
<input type="button" class="format" title="Text Justify" onclick="tag('text-justify')" value="☰" />
</div>
<textarea id="body" placeholder="Post body"></textarea>
<button onclick="sendPost()">Publish</button>
<div class="log" id="log"></div>
</div>
<script>
const client = new dsteem.Client('https://api.steemit.com');
async function sendPost() {
const author = document.getElementById('author').value.trim();
const postingKey = document.getElementById('key').value.trim();
const title = document.getElementById('title').value.trim();
const body = document.getElementById('body').value.trim();
const tagsInput = document.getElementById('tags').value.trim();
// Split tags to array
const tags = tagsInput.split(' ').map(tag => tag.toLowerCase()).filter(Boolean);
// Require at least one tag
if (tags.length === 0) {
alert("❗ Please provide at least one tag (space separated).");
return;
}
const parentPermlink = tags[0];
const permlink = title.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '') || 'post-' + Date.now();
const key = dsteem.PrivateKey.fromString(postingKey);
const comment = {
parent_author: '',
parent_permlink: parentPermlink,
author,
permlink,
title,
body,
json_metadata: JSON.stringify({
tags: tags,
app: 'script/0.1',
format: 'markdown'
})
};
try {
await client.broadcast.comment(comment, key);
const url = `https://steemit.com/${encodeURIComponent(parentPermlink)}/@${author}/${permlink}`;
document.getElementById('log').textContent = '✅ Post published!\n' + url;
} catch (e) {
document.getElementById('log').textContent = '❌ Error: ' + e.message;
console.error(e);
}
}
// Basic HTML textarea editor
function tag(tag) {
var src = document.getElementById('body');
var start, end, url;
switch (tag) {
case 'link':
url = prompt("Insert URL", '');
if (url != null) {
start = '<a href="' + url + '">';
end = '</a>';
} else {
start = '';
end = '';
}
break;
case 'align-right':
start = '<div class="pull-right">';
end = '</div>';
break;
case 'align-left':
start = '<div class="pull-left">';
end = '</div>';
break;
case 'text-rtl':
start = '<div class="text-rtl">';
end = '</div>';
break;
case 'text-justify':
start = '<div class="text-justify">';
end = '</div>';
break;
default:
start = '<' + tag + '>';
end = '</' + tag + '>';
}
if (!src.setSelectionRange) {
var selected = document.selection.createRange().text;
src.focus();
var codetext = selected.length <= 0 ? start + end : start + selected + end;
document.selection.createRange().text = codetext;
} else {
var scrollTop = src.scrollTop;
var pretext = src.value.substring(0, src.selectionStart);
var selectedText = src.value.substring(src.selectionStart, src.selectionEnd);
var codetext = start + selectedText + end;
var posttext = src.value.substring(src.selectionEnd, src.value.length);
src.value = pretext + codetext + posttext;
src.scrollTop = scrollTop;
src.selectionStart = pretext.length;
src.selectionEnd = pretext.length + codetext.length;
src.focus();
}
}
</script>
</body>
</html>
Дякую за увагу!
Взагалі чудово, що доступ до блокчейну можна отримувати різноманітними способами й під це діло прикручувати різні інстурменти. HTML теги нікого не зацікавили, бо P в div загортає автоматом із віузального редактора. Тобто для цього достатньо лише відступ в один ряд. Що підходить для більшості.
І без цих тегів легше, проте для вирівнювання тексту не скрізь є, лише на steempro додано, хоча не часто його й використовують (як на мою думку). А от додавання зображень із обтікання текстом або щоб декілька в ряю, це не зробили зручно. Тобто там є шаблони для вставки, але ж купа зайвого і не так зручно. Це такі висновки на основі того, що бачу.
Дякую за коментар! Ви говорите про дуже логічні речі. Я подивився на свій допис трохи під іншим кутом і справді, з усього описаного лише три блочні елементи можуть бути відчутно корисними з практичної точки зору користувача.
До альтернативних платформ я ставлюся з часткою скепсису. Може навіть даремно... Справа у звичці сидіти на steemit.com :)
Це лише приклад, завертання в html теги, на steemit теж саме. Якщо скрип працює автономно для відправки допису, без веб посередника steemit.com чи іншого, то ймовірно, без html тегів не обійтись.