layout: update navigation and add icons

Used different CSS-only tab technique. The implementation before
messed up the whole height of the page and used unnecessary
absolute positioning.

Added comment icon from MFG Labs iconset (SIL)
- https://github.com/MfgLabs/mfglabs-iconset

Added outlined and filled heart from Elusiv icons (SIL)
- https://github.com/dovy/elusive-iconfont

Time ago seconds should be rounded.
OFF0 2 years ago
parent a71de21302
commit 13b3db4302
Signed by: offbyn
GPG Key ID: 94A2F643C51F37FA

@ -9,7 +9,9 @@ export const options = {
'src/main.js',
'src/main.css',
'src/index.html',
'src/bubble.svg'
'src/assets/bubble.svg',
'src/assets/comment.svg',
'src/assets/heart-fill.svg',
],
outdir: 'dist',
//entryNames: '[name]-[hash]', TODO: replace urls in index.html with hashed paths

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="21" viewBox="0 0 80.035 70.031"><path fill="currentColor" d="M0 31.347q0-5.113 2.02-9.875 2.019-4.761 5.724-8.633 3.705-3.872 8.615-6.762 4.91-2.89 11.023-4.484Q33.496 0 40.018 0q6.52 0 12.635 1.593 6.113 1.594 11.023 4.484 4.91 2.89 8.615 6.762 3.705 3.872 5.725 8.633 2.019 4.762 2.019 9.875 0 6.373-3.168 12.153t-8.522 9.968q-5.354 4.187-12.765 6.67-7.41 2.482-15.562 2.482-7.967 0-15.303-2.371l-9.116 6.447q-5.113 3.223-7.188 1.871-2.075-1.352-.926-7.614l1.89-9.486q-4.484-4.15-6.93-9.3Q0 37.017 0 31.347Z" style="stroke-width:.0370534"/></svg>

After

Width:  |  Height:  |  Size: 607 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M14 20.408c-.492.308-.903.546-1.192.709-.153.086-.308.17-.463.252h-.002a.75.75 0 01-.686 0 16.709 16.709 0 01-.465-.252 31.147 31.147 0 01-4.803-3.34C3.8 15.572 1 12.331 1 8.513 1 5.052 3.829 2.5 6.736 2.5 9.03 2.5 10.881 3.726 12 5.605 13.12 3.726 14.97 2.5 17.264 2.5 20.17 2.5 23 5.052 23 8.514c0 3.818-2.801 7.06-5.389 9.262A31.146 31.146 0 0114 20.408z"></path></svg>

After

Width:  |  Height:  |  Size: 464 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill-rule="evenodd" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"></path></svg>

After

Width:  |  Height:  |  Size: 734 B

@ -8,13 +8,14 @@
}
.mbox-img {
--size: 4ch;
--size: 5rem;
align-self: start;
flex-basis: var(--size);
height: var(--size);
margin-right: 1rem;
margin-top: .5ch;
max-width: var(--size);
max-width: var(--size);
}
.mbox-recommend-server .mbox-img {
--size: 2.5ch;
@ -38,7 +39,6 @@
}
.mbox-header time,
.mbox-username {
color: var(--color-accent);
}
.mbox-recommend-server .mbox-body {

@ -13,7 +13,7 @@
export function elem(name = 'div', {data, ...props} = {}, children = []) {
const el = document.createElement(name);
Object.assign(el, props);
if (typeof children === 'string') {
if (['number', 'string'].includes(typeof children)) {
el.append(children);
} else {
el.append(...children);

@ -4,7 +4,6 @@ form {
input,
textarea {
color: var(--color);
font-family: monospace;
font-size: 1.6rem;
margin-bottom: 1.2rem;
padding: 1.3rem 1.8rem;
@ -12,8 +11,8 @@ textarea {
button,
label {
cursor: pointer;
display: block;
font-family: monospace;
font-size: 1.6rem;
margin-bottom: 0;
padding: 1.3rem 1.8rem;
@ -34,8 +33,8 @@ input[type="text"] {
}
input[type="password"]:focus,
input[type="text"]:focus {
border-color: #d4d4d4;
outline-offset: 1px;
border-color: var(--focus-border-color);
outline-offset: 2px;
}
.buttons {
@ -58,11 +57,18 @@ button {
button:focus {
}
.button-inline {
.btn-inline {
align-items: center;
background: transparent;
color: var(--color);
display: inline;
padding: .3rem;
display: inline-flex;
gap: .5ch;
line-height: 1;
padding: .6rem;
}
.btn-inline img {
max-height: 18px;
max-width: 18px;
}
button:disabled {

@ -6,14 +6,22 @@
<link rel="stylesheet" href="main.css" type="text/css">
</head>
<body>
<div class="tabs">
<div class="tab">
<input type="radio" name="maintabs" id="homefeed" checked>
<label for="homefeed">feed</label>
<div class="content">
<main class="tabbed">
<input type="radio" name="maintabs" id="feed" class="tab" checked>
<label for="feed">feed</label>
<input type="radio" name="maintabs" id="trending" class="tab">
<label for="trending">trending</label>
<input type="radio" name="maintabs" id="direct" class="tab">
<label for="direct">direct</label>
<input type="radio" name="maintabs" id="chat" class="tab">
<label for="chat">chat</label>
<input type="radio" name="maintabs" id="settings" class="tab">
<label for="settings">settings</label>
<div class="tabs">
<div class="tab-content">
<article class="mbox">
<img class="mbox-img" src="bubble.svg">
<img class="mbox-img" src="assets/comment.svg" alt="">
<div class="mbox-body" id="newMessage">
<form class="form-inline" id="writeForm">
<input type="text" name="message">
@ -22,57 +30,43 @@
<small id="sendstatus" class="form-status"></small>
</div>
</article>
<div class="cards" id="feed"></div>
<div class="cards" id="homefeed"></div>
</div>
</div>
<div class="tab">
<input type="radio" name="maintabs" id="trending">
<label for="trending">trending</label>
<div class="content">
<div class="tab-content">
<p><a href="https://github.com/nostr-protocol/nips/blob/master/12.md">NIP-12 (generic queries)</a></p>
</div>
</div>
<div class="tab">
<input type="radio" name="maintabs" id="direct">
<label for="direct">direct</label>
<div class="content">
<div class="tab-content">
<p><a href="https://github.com/nostr-protocol/nips/blob/master/04.md">NIP-04 (direct msg)</a></p>
</div>
</div>
<div class="tab">
<input type="radio" name="maintabs" id="chat">
<label for="chat">chat</label>
<div class="content">
<div class="tab-content">
<p><a href="https://github.com/nostr-protocol/nips/blob/master/28.md">NIP-28 (public chat)</a></p>
</div>
<div class="tab-content">
<form action="#" name="settings">
<label for="pubkey">public-key</label>
<input type="text" id="pubkey">
<label for="privatekey">
private-key
<button type="button" name="privatekey-toggle" class="btn-inline" >
<small>show</small>
</button>
</label>
<input type="password" id="privatekey">
<div class="buttons">
<small id="keystatus" class="form-status" hidden></small>
<button type="button" name="generate" tabindex="0">new</button>
<span class="inline-text"></span>
<button type="button" name="import" tabindex="0" disabled>save</button>
</div>
</form>
</div>
</div>
</main>
<div class="tab">
<input type="radio" name="maintabs" id="settings">
<label for="settings">settings</label>
<form action="#" name="settings" class="content">
<label for="pubkey">public-key</label>
<input type="text" id="pubkey">
<label for="privatekey">
private-key
<button type="button" name="privatekey-toggle" class="button-inline" >
<small>show</small>
</button>
</label>
<input type="password" id="privatekey">
<div class="buttons">
<small id="keystatus" class="form-status" hidden></small>
<button type="button" name="generate" tabindex="0">new</button>
<span class="inline-text"></span>
<button type="button" name="import" tabindex="0" disabled>save</button>
</div>
</form>
</div>
</div>
</body>
<script src="main.js"></script>
</html>

@ -3,6 +3,13 @@
@import "form.css";
:root {
/* 5px auto Highlight */
--focus-border-color: rgb(0, 122, 255);
--focus-border-radius: 2px;
--focus-outline-color: rgb(127, 189, 247);
--focus-outline-style: solid;
--focus-outline-width: 2px;
--focus-outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
--font-small: 1.2rem;
}
@ -53,20 +60,35 @@ html {
body {
background-color: var(--bgcolor);
color: var(--color);
font-family: monospace;
font-size: 1.6rem;
line-height: 1.5;
}
body,
button,
input,
select,
textarea {
font-family: monospace;
}
small,
time {
font-size: var(--font-small);
}
*, ::after, ::before {
box-sizing: border-box;
}
.danger {
background-color: var(--bgcolor-danger);
}
a:focus {
border-radius: var(--focus-border-radius);
outline: var(--focus-outline);
outline-offset: 0;
}
img[alt] {
font-size: .9rem;
text-align: center;
word-break: break-all;
}

@ -75,7 +75,7 @@ function handleTextNote(evt, relay) {
}
// feed
const feedContainer = document.querySelector('#feed');
const feedContainer = document.querySelector('#homefeed');
const feedDomMap = {};
const sortByCreatedAt = (evt1, evt2) => {
if (evt1.created_at === evt2.created_at) {
@ -115,28 +115,31 @@ setInterval(() => {
function createTextNote(evt, relay) {
const {host, img, isReply, replies, time, userName} = getMetadata(evt, relay);
const name = elem('strong', {className: 'mbox-username', title: evt.pubkey}, userName);
const timeElem = elem('time', { dateTime: time.toISOString()}, formatTime(time));
const hasLongContent = evt.content.length > 280;
const headerInfo = isReply
? [name, ' ', timeElem]
: [name, ` on ${host} `, timeElem];
const content = hasLongContent ? `${evt.content.slice(0, 280)}` : evt.content;
const isLongContent = evt.content.length > 280;
const content = isLongContent ? `${evt.content.slice(0, 280)}` : evt.content;
const body = elem('div', {className: 'mbox-body'}, [
elem('header', {
className: 'mbox-header',
title: `Event ${evt.id}\non ${host} ${time}
${isReply ? `\nReply ${evt.tags[0][1]}\n` : ''}`
title: `User: ${userName}\n${time}\n\nUser pubkey: ${evt.pubkey}\n\nRelay: ${host}\n\nEvent-id: ${evt.id}
${isReply ? `\nReply to ${evt.tags[0][1]}\n` : ''}`
}, [
elem('small', {}, headerInfo),
elem('small', {}, [
elem('strong', {className: 'mbox-username'}, userName),
' ',
elem('time', {dateTime: time.toISOString()}, formatTime(time))
]),
]),
elem('div', {data: hasLongContent ? {append: evt.content.slice(280)} : null}, content),
elem('div', {data: isLongContent ? {append: evt.content.slice(280)} : null}, content),
elem('button', {
className: 'btn-inline', name: 'reply', type: 'button',
data: {'eventId': evt.id, relay},
}, [elem('img', {height: 24, width: 24, src: 'assets/comment.svg'})]),
elem('button', {
className: 'button-inline',
name: 'reply', type: 'button',
data: {'eventId': evt.id, relay}
className: 'btn-inline', name: 'star', type: 'button',
data: {'eventId': evt.id, relay},
}, [
elem('small', {}, 'reply')
elem('img', {alt: '♥', height: 24, width: 24, src: 'assets/heart-fill.svg'}),
elem('small', {}, 2),
]),
replies[0] ? elem('div', {className: 'mobx-replies'}, replies.map(e => createTextNote(e, relay))) : '',
]);
@ -242,15 +245,14 @@ const getHost = (url) => {
function getMetadata(evt, relay) {
const host = getHost(relay);
const user = userList.find(user => user.pubkey === evt.pubkey);
const userImg = /*user?.metadata[relay]?.picture || */'bubble.svg'; // TODO: enable pic once we have proxy
const userImg = /*user?.metadata[relay]?.picture || */'assets/bubble.svg'; // TODO: enable pic once we have proxy
const userName = user?.metadata[relay]?.name || evt.pubkey.slice(0, 8);
const userAbout = user?.metadata[relay]?.about || '';
const title = `${userName} on ${host} ${userAbout}`;
const img = elem('img', {
className: 'mbox-img',
src: userImg,
alt: title,
title,
alt: `${userName} ${host}`,
title: `${userName} on ${host} ${userAbout}`,
}, '');
const isReply = evt.tags.some(hasEventTag);
const replies = replyList.filter((reply) => reply.tags[0][1] === evt.id);
@ -390,7 +392,7 @@ function validKeys(privatekey, pubkey) {
}
statusMessage.hidden = false;
importBtn.setAttribute('disabled', true);
return false;
return false;
}
privateTgl.addEventListener('click', () => {

@ -1,19 +1,12 @@
.tabs {
position: relative;
max-width: 96ch;
min-height: 200px;
}
.tab {
float: left;
}
.tabs .tab-content { display: none; }
#feed:checked ~ .tabs .tab-content:nth-child(1),
#trending:checked ~ .tabs .tab-content:nth-child(2),
#direct:checked ~ .tabs .tab-content:nth-child(3),
#chat:checked ~ .tabs .tab-content:nth-child(4),
#settings:checked ~ .tabs .tab-content:nth-child(5) { display: block; }
.tab > label {
cursor: pointer;
padding: 1rem 1.5em;
}
.tab [type=radio] {
input[type="radio"].tab {
clip: rect(0, 0, 0, 0);
height: 0;
overflow: hidden;
@ -21,33 +14,45 @@
width: 0;
}
.tab [type=radio] + label {
.tab + label {
border: none;
color: var(--color);
display: inline-block;
outline: 2px solid var(--bgcolor-accent);
outline-offset: -1px;
padding: 1rem 1.5em;
position: relative;
top: 1px;
}
input[type="radio"]:checked + label {
background: var(--bgcolor-accent);
}
/*
.tab [type=radio]:focus + label {
outline: 2px dotted black;
.tab:focus + label,
.tab:active + label {
border-color: var(--focus-border-color);
border-radius: var(--focus-border-radius);
outline: var(--focus-outline);
outline-offset: var(--focus-outline-offset);
}
*/
.tab [type=radio]:checked ~ label {
background-color: var(--bgcolor-accent);
color: var(--color);
z-index: 2;
.tab-content {
max-width: 96ch;
min-height: 200px;
}
/*
.tabs {
position: relative;
}
.tab [type=radio]:checked ~ label ~ .content {
opacity: 1;
z-index: 1;
.tab {
float: left;
}
.tab .content {
bottom: 0;
left: 0;
opacity: 0;
position: absolute;
right: 0;
top: 10rem;
.tab > label {
}
*/

@ -45,7 +45,7 @@ const timeAgo = (time, locale = 'en') => {
} else if (minutes > 0) {
return relativeTime.format(0 - minutes, 'minute');
} else {
return relativeTime.format(0 - timeSince, 'second');
return relativeTime.format(Math.round(0 - timeSince), 'second');
}
};

Loading…
Cancel
Save