From 13b3db4302d021b4a1e661941b91469a1efcec11 Mon Sep 17 00:00:00 2001 From: OFF0 Date: Sat, 19 Nov 2022 19:27:33 +0100 Subject: [PATCH] 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. --- esbuildconf.js | 4 +- src/{ => assets}/bubble.svg | 0 src/assets/comment.svg | 1 + src/assets/heart-fill.svg | 1 + src/assets/heart.svg | 1 + src/cards.css | 4 +- src/domutil.js | 2 +- src/form.css | 20 +++++--- src/index.html | 92 +++++++++++++++++-------------------- src/main.css | 32 +++++++++++-- src/main.js | 44 +++++++++--------- src/tabs.css | 73 +++++++++++++++-------------- src/timeutil.js | 2 +- 13 files changed, 155 insertions(+), 121 deletions(-) rename src/{ => assets}/bubble.svg (100%) create mode 100644 src/assets/comment.svg create mode 100644 src/assets/heart-fill.svg create mode 100644 src/assets/heart.svg diff --git a/esbuildconf.js b/esbuildconf.js index 0988ebd..4428cd9 100644 --- a/esbuildconf.js +++ b/esbuildconf.js @@ -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 diff --git a/src/bubble.svg b/src/assets/bubble.svg similarity index 100% rename from src/bubble.svg rename to src/assets/bubble.svg diff --git a/src/assets/comment.svg b/src/assets/comment.svg new file mode 100644 index 0000000..1936a6d --- /dev/null +++ b/src/assets/comment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/heart-fill.svg b/src/assets/heart-fill.svg new file mode 100644 index 0000000..2c1f312 --- /dev/null +++ b/src/assets/heart-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/heart.svg b/src/assets/heart.svg new file mode 100644 index 0000000..b142374 --- /dev/null +++ b/src/assets/heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/cards.css b/src/cards.css index 39fce5d..faa77c2 100644 --- a/src/cards.css +++ b/src/cards.css @@ -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 { diff --git a/src/domutil.js b/src/domutil.js index 2a0f656..8c780a5 100644 --- a/src/domutil.js +++ b/src/domutil.js @@ -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); diff --git a/src/form.css b/src/form.css index 605a141..36324e6 100644 --- a/src/form.css +++ b/src/form.css @@ -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 { diff --git a/src/index.html b/src/index.html index b939838..8b81010 100644 --- a/src/index.html +++ b/src/index.html @@ -6,14 +6,22 @@ -
- -
- - -
+
+ + + + + + + + + + + +
+
- +
@@ -22,57 +30,43 @@
-
+
-
- -
- - - - -
- - - - -
- - -
+ + + +
+ + + + + +
+ + + + +
+ +
+
-
- - -
- - - - -
- - - - -
-
-
- -
diff --git a/src/main.css b/src/main.css index e77ab5d..7052d73 100644 --- a/src/main.css +++ b/src/main.css @@ -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; +} diff --git a/src/main.js b/src/main.js index 7226b4a..9863ecb 100644 --- a/src/main.js +++ b/src/main.js @@ -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', () => { diff --git a/src/tabs.css b/src/tabs.css index 4942a37..2edcedf 100644 --- a/src/tabs.css +++ b/src/tabs.css @@ -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 { } + + +*/ \ No newline at end of file diff --git a/src/timeutil.js b/src/timeutil.js index 810a4f9..f798f83 100644 --- a/src/timeutil.js +++ b/src/timeutil.js @@ -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'); } };