From e06c3b2e7a773767403afb8bf3a6dde73d265fdb Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Thu, 19 Jan 2023 00:31:34 +0100 Subject: [PATCH 1/7] UI refactor --- src/Number.ts | 9 +++ src/element/FollowsYou.tsx | 3 +- src/element/Invoice.css | 8 +-- src/element/LNURLTip.css | 21 ++++++- src/element/LNURLTip.tsx | 4 +- src/element/Note.css | 110 ++++++++++++++++++++++++++++++----- src/element/Note.tsx | 2 +- src/element/NoteCreator.tsx | 2 +- src/element/NoteFooter.tsx | 77 +++++++++++++++--------- src/element/NoteReaction.css | 25 ++++---- src/element/NoteReaction.tsx | 7 --- src/element/NoteTime.tsx | 7 +-- src/element/Text.css | 35 +++++++++++ src/element/Text.tsx | 18 +++++- src/element/ZapButton.tsx | 4 +- src/index.css | 54 +++++++---------- src/pages/ChatPage.css | 2 +- src/pages/ProfilePage.tsx | 6 +- 18 files changed, 272 insertions(+), 122 deletions(-) create mode 100644 src/Number.ts diff --git a/src/Number.ts b/src/Number.ts new file mode 100644 index 00000000..551f48b2 --- /dev/null +++ b/src/Number.ts @@ -0,0 +1,9 @@ +export function formatShort(n: number) { + if (n < 999) { + return n + } else if (n < 1e8) { + return `${Math.floor(n / 1e3)}K` + } else { + return `${Math.floor(n / 1e6)}M` + } +} diff --git a/src/element/FollowsYou.tsx b/src/element/FollowsYou.tsx index 88280e27..c9cf63af 100644 --- a/src/element/FollowsYou.tsx +++ b/src/element/FollowsYou.tsx @@ -1,3 +1,4 @@ +import "./FollowsYou.css"; import { useMemo } from "react"; import { useSelector } from "react-redux"; import { HexKey } from "../nostr"; @@ -21,7 +22,7 @@ export default function FollowsYou({ pubkey }: FollowsYouProps ) { return ( <> - { followsMe ? follows you : null } + { followsMe ? follows you : null } ) } diff --git a/src/element/Invoice.css b/src/element/Invoice.css index 04dbf0b1..d01fd6c5 100644 --- a/src/element/Invoice.css +++ b/src/element/Invoice.css @@ -1,7 +1,7 @@ .note-invoice { - background: var(--bg-color); - border-radius: 10px; - border: 1px solid var(--gray-tertiary); + background: var(--note-bg-color); + border: 1px solid var(--font-secondary-color); + border-radius: 16px; padding: 12px; margin: 10px auto; } @@ -11,5 +11,5 @@ } .note-invoice small { - color: var(--gray-medium); + color: var(--font-secondary-color); } diff --git a/src/element/LNURLTip.css b/src/element/LNURLTip.css index 3c0d1b73..f6d3d68a 100644 --- a/src/element/LNURLTip.css +++ b/src/element/LNURLTip.css @@ -17,9 +17,24 @@ background-color: var(--gray); } -.lnurl-tip .pill.active { - color: var(--bg-color); - background-color: var(--font-color); +.sat-amount { + display: inline-block; + background-color: var(--gray-secondary); + color: var(--font-color); + padding: 2px 10px; + border-radius: 10px; + user-select: none; + margin: 2px 5px; +} + +.sat-amount:hover { + cursor: pointer; +} + +.sat-amount.active { + font-weight: bold; + color: var(--note-bg); + background-color: var(--font-color); } .lnurl-tip .invoice { diff --git a/src/element/LNURLTip.tsx b/src/element/LNURLTip.tsx index 0d947ded..bf5652ba 100644 --- a/src/element/LNURLTip.tsx +++ b/src/element/LNURLTip.tsx @@ -188,11 +188,11 @@ export default function LNURLTip(props: LNURLTipProps) { setComment(e.target.value)} /> : null}
- {serviceAmounts.map(a => selectAmount(a)}> + {serviceAmounts.map(a => selectAmount(a)}> {a.toLocaleString()} )} {payService ? - selectAmount(-1)}> + selectAmount(-1)}> Custom : null}
diff --git a/src/element/Note.css b/src/element/Note.css index 85c87c43..aa097402 100644 --- a/src/element/Note.css +++ b/src/element/Note.css @@ -1,8 +1,8 @@ .note { - margin-bottom: 10px; - border-radius: 10px; + margin-bottom: 24px; + border-radius: 16px; background-color: var(--note-bg); - padding: 10px 10px 8px 10px; + padding: 24px; min-height: 110px; } @@ -10,26 +10,28 @@ border-bottom: none; } -.note > .header > .pfp { - flex-grow: 1; +.note .header { + display: flex; + flex-direction: row; + justify-content: space-between; } .note > .header .reply { - font-size: 12px; - color: var(--gray-light); + font-size: var(--font-size-tiny); + color: var(--font-secondary-color); } .note > .header > .info { - font-size: 10px; + font-size: var(--font-size); white-space: nowrap; - color: var(--gray-light); - align-self: flex-start; + color: var(--font-secondary-color); } .note > .body { - padding: 10px 5px; - white-space: pre-wrap; + margin-top: 12px; overflow: hidden; + text-overflow: ellipsis; + white-space: pre-wrap; word-break: normal; } @@ -38,7 +40,26 @@ } .note > .footer { - text-align: right; + display: flex; + flex-direction: row-reverse; + margin-top: 24px; +} + +.note > .note-creator { + margin-top: 24px; +} + +.thread.note { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.thread.note, .indented .note { + margin-bottom: 0; +} + +.indented .note { + border-radius: 0; } .indented { @@ -46,8 +67,69 @@ padding-left: 2px; } +.note:last-child { + border-bottom-right-radius: 16px; + margin-bottom: 24px; +} + +.indented > .indented .note:last-child { + border-bottom-right-radius: 0px; + margin-bottom: 0; +} + .indented .active { background-color: var(--gray-tertiary); margin-left: -5px; border-left: 3px solid var(--highlight); -} \ No newline at end of file + border-radius: 0; +} + +.reaction-pill { + display: flex; + flex-direction: row; + padding: 2px 10px; + border-radius: 10px; + user-select: none; + color: var(--font-secondary-color); +} + +.reaction-pill .reaction-pill-number { + margin-left: 8px; +} + +.reaction-pill.reacted { + color: var(--highlight); +} + +.reaction-pill:hover { + cursor: pointer; +} + +.note.active > .header .reply { + color: var(--font-tertiary-color); +} + +.note.active > .header > .info { + color: var(--font-tertiary-color); +} + +.note.active > .footer > .reaction-pill { + color: var(--font-tertiary-color); +} + +@media (prefers-color-scheme: light) { + .indented .active { + background-color: var(--gray-secondary); + } + .note.active > .header .reply { + color: var(--font-secondary-color); + } + + .note.active > .header > .info { + color: var(--font-secondary-color); + } + + .note.active > .footer > .reaction-pill { + color: var(--font-seco666ndary-color); + } +} diff --git a/src/element/Note.tsx b/src/element/Note.tsx index 3e85bea7..f2e8e07d 100644 --- a/src/element/Note.tsx +++ b/src/element/Note.tsx @@ -78,7 +78,7 @@ export default function Note(props: NoteProps) { let pubMentions = mentions.length > maxMentions ? `${mentions?.slice(0, maxMentions).join(", ")} & ${othersLength} other${othersLength > 1 ? 's' : ''}` : mentions?.join(", "); return (
- ➡️ {(pubMentions?.length ?? 0) > 0 ? pubMentions : replyId ? hexToBech32("note", replyId)?.substring(0, 12) : ""} + {(pubMentions?.length ?? 0) > 0 ? pubMentions : replyId ? hexToBech32("note", replyId)?.substring(0, 12) : ""}
) } diff --git a/src/element/NoteCreator.tsx b/src/element/NoteCreator.tsx index f3a3f1de..442f7671 100644 --- a/src/element/NoteCreator.tsx +++ b/src/element/NoteCreator.tsx @@ -100,4 +100,4 @@ export function NoteCreator(props: NoteCreatorProps) { ); -} \ No newline at end of file +} diff --git a/src/element/NoteFooter.tsx b/src/element/NoteFooter.tsx index 22a234fa..afb6a6b4 100644 --- a/src/element/NoteFooter.tsx +++ b/src/element/NoteFooter.tsx @@ -3,6 +3,7 @@ import { useSelector } from "react-redux"; import { faHeart, faReply, faThumbsDown, faTrash, faBolt, faRepeat } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { formatShort } from "../Number"; import useEventPublisher from "../feed/EventPublisher"; import { getReactions, normalizeReaction, Reaction } from "../Util"; import { NoteCreator } from "./NoteCreator"; @@ -29,7 +30,6 @@ export default function NoteFooter(props: NoteFooterProps) { const isMine = ev.RootPubKey === login; const reactions = useMemo(() => getReactions(related, ev.Id, EventKind.Reaction), [related]); const reposts = useMemo(() => getReactions(related, ev.Id, EventKind.Repost), [related]); - const groupReactions = useMemo(() => { return reactions?.reduce((acc, { content }) => { let r = normalizeReaction(content); @@ -50,8 +50,10 @@ export default function NoteFooter(props: NoteFooterProps) { } async function react(content: string) { + if (!hasReacted(content)) { let evLike = await publisher.react(ev, content); publisher.broadcast(evLike); + } } async function deleteEvent() { @@ -73,9 +75,11 @@ export default function NoteFooter(props: NoteFooterProps) { if (service) { return ( <> - setTip(true)}> - - +
setTip(true)}> +
+ +
+
) } @@ -85,10 +89,10 @@ export default function NoteFooter(props: NoteFooterProps) { function reactionIcon(content: string, reacted: boolean) { switch (content) { case Reaction.Positive: { - return ; + return ; } case Reaction.Negative: { - return ; + return ; } } return content; @@ -97,30 +101,45 @@ export default function NoteFooter(props: NoteFooterProps) { return ( <>
- {isMine ? - deleteEvent()} /> - : null} - {tipButton()} - repost()}> - - {reposts.length > 0 ? <> {reposts.length} : null} - - setReply(s => !s)}> + {isMine && ( +
+
+ deleteEvent()} /> +
+
+ )} +
setReply(s => !s)}> +
- - {Object.keys(groupReactions || {}).map((emoji) => { - let didReact = hasReacted(emoji); - return ( - { - if (!didReact) { - react(emoji); - } - }} key={emoji}> - {reactionIcon(emoji, didReact)} - {groupReactions[emoji] ? <> {groupReactions[emoji]} : null} - - ) - })} +
+
+
repost()}> +
+ +
+ {reposts.length > 0 && ( +
+ {formatShort(reposts.length)} +
+ )} +
+
react("+")}> +
+ +
+
+ {formatShort(groupReactions[Reaction.Positive])} +
+
+
react("-")}> +
+ +
+
+ {formatShort(groupReactions[Reaction.Negative])} +
+
+ {tipButton()}
.note { margin: 10px 20px; - border: 1px solid var(--gray); - border-radius: 10px; - padding: 10px; } -.reaction > .header > .pfp { - flex-grow: 1; +.reaction > .header { + display: flex; + flex-direction: row; + justify-content: space-between; } .reaction > .header .reply { - font-size: small; + font-size: var(--font-size-small); } .reaction > .header > .info { - color: var(--gray-light); - font-size: 10px; - align-self: flex-start; -} - -.reaction .reaction-text { - margin-left: .2em; - color: var(--gray-light); + font-size: var(--font-size); + white-space: nowrap; + color: var(--font-secondary-color); + margin-right: 24px; } diff --git a/src/element/NoteReaction.tsx b/src/element/NoteReaction.tsx index bd43b3a0..c199df59 100644 --- a/src/element/NoteReaction.tsx +++ b/src/element/NoteReaction.tsx @@ -44,13 +44,6 @@ export default function NoteReaction(props: NoteReactionProps) { } } - function tagLine() { - switch (ev.Kind) { - case EventKind.Reaction: return reacted with {mapReaction(ev.Content)}; - case EventKind.Repost: return reposted - } - } - /** * Some clients embed the reposted note in the content */ diff --git a/src/element/NoteTime.tsx b/src/element/NoteTime.tsx index ae17dd4c..5e27f906 100644 --- a/src/element/NoteTime.tsx +++ b/src/element/NoteTime.tsx @@ -25,11 +25,10 @@ export default function NoteTime(props: NoteTimeProps) { return fallback } else { let mins = Math.floor(absAgo / MinuteInMs); - let minutes = mins === 1 ? 'min' : 'mins' if(ago < 0) { - return `in ${mins} ${minutes}`; + return `in ${mins}m`; } - return `${mins} ${minutes} ago`; + return `${mins}m`; } } @@ -48,4 +47,4 @@ export default function NoteTime(props: NoteTimeProps) { }, [from]); return <>{time} -} \ No newline at end of file +} diff --git a/src/element/Text.css b/src/element/Text.css index 6d3c7b8f..10086dfb 100644 --- a/src/element/Text.css +++ b/src/element/Text.css @@ -52,9 +52,44 @@ max-height: 500px; margin: 10px auto; display: block; + border-radius: 12px; } .text iframe, .text video { width: -webkit-fill-available; aspect-ratio: 16 / 9; } + +.text .truncate { + display: inline-block; + margin: 0; + max-width: 260px; + white-space: nowrap; + text-overflow: ellipsis; + color: var(--highlight); + height: var(--font-size); +} + +@media (min-width: 420px) { + .text .truncate { + max-width: 300px; + } +} + +@media (min-width: 520px) { + .text .truncate { + max-width: 360px; + } +} + +@media (min-width: 720px) { + .text .truncate { + max-width: 420px; + } +} + +@media (min-width: 1024px) { + .text .truncate { + max-width: 600px; + } +} diff --git a/src/element/Text.tsx b/src/element/Text.tsx index ee28313e..3019f4d1 100644 --- a/src/element/Text.tsx +++ b/src/element/Text.tsx @@ -37,7 +37,11 @@ function transformHttpLink(a: string) { return