diff --git a/package.json b/package.json
index 0df6806b..5f260460 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,6 @@
"@reduxjs/toolkit": "^1.9.1",
"bech32": "^2.0.0",
"light-bolt11-decoder": "^2.1.0",
- "moment": "^2.29.4",
"qr-code-styling": "^1.6.0-rc.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
diff --git a/src/Text.js b/src/Text.js
index 382376b3..58a3c151 100644
--- a/src/Text.js
+++ b/src/Text.js
@@ -2,14 +2,13 @@ import { Link } from "react-router-dom";
import Invoice from "./element/Invoice";
import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex } from "./Const";
-import { eventLink, profileLink } from "./Util";
+import { eventLink, hexToBech32, profileLink } from "./Util";
function transformHttpLink(a) {
try {
const url = new URL(a);
- const vParam = url.searchParams.get('v')
- const youtubeId = YoutubeUrlRegex.test(a) && RegExp.$1
- const extension = FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1
+ const youtubeId = YoutubeUrlRegex.test(a) && RegExp.$1;
+ const extension = FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1;
if (extension) {
switch (extension) {
case "gif":
@@ -28,7 +27,7 @@ function transformHttpLink(a) {
return
}
default:
- return {url.toString()}
+ return e.stopPropagation()}>{url.toString()}
}
} else if (youtubeId) {
return (
@@ -37,7 +36,7 @@ function transformHttpLink(a) {
@@ -45,7 +44,7 @@ function transformHttpLink(a) {
>
)
} else {
- return {url.toString()}
+ return e.stopPropagation()}>{url.toString()}
}
} catch (e) {
console.warn(`Not a valid url: ${a}`);
@@ -77,12 +76,12 @@ export function extractMentions(fragments, tags, users) {
if (ref) {
switch (ref.Key) {
case "p": {
- let pUser = users[ref.PubKey]?.name ?? ref.PubKey.substring(0, 8);
- return ev.stopPropagation()}>@{pUser};
+ let pUser = users[ref.PubKey]?.name ?? hexToBech32("npub", ref.PubKey).substring(0, 12);
+ return e.stopPropagation()}>@{pUser};
}
case "e": {
- let eText = ref.Event.substring(0, 8);
- return #{eText};
+ let eText = hexToBech32("note", ref.Event).substring(0, 12);
+ return e.stopPropagation()}>#{eText};
}
}
}
diff --git a/src/Util.js b/src/Util.js
index 634a7181..7096b794 100644
--- a/src/Util.js
+++ b/src/Util.js
@@ -50,8 +50,16 @@ export function bech32ToText(str) {
* @returns
*/
export function eventLink(hex) {
+ return `/e/${hexToBech32("note", hex)}`;
+}
+
+/**
+ * Convert hex to bech32
+ * @param {string} hex
+ */
+export function hexToBech32(hrp, hex) {
let buf = secp.utils.hexToBytes(hex);
- return `/e/${bech32.encode("note", bech32.toWords(buf))}`;
+ return bech32.encode(hrp, bech32.toWords(buf));
}
/**
@@ -60,8 +68,7 @@ export function eventLink(hex) {
* @returns
*/
export function profileLink(hex) {
- let buf = secp.utils.hexToBytes(hex);
- return `/p/${bech32.encode("npub", bech32.toWords(buf))}`;
+ return `/p/${hexToBech32("npub", hex)}`;
}
/**
diff --git a/src/element/Invoice.js b/src/element/Invoice.js
index 1e00e65e..f96a7b0b 100644
--- a/src/element/Invoice.js
+++ b/src/element/Invoice.js
@@ -1,7 +1,7 @@
import "./Invoice.css";
import { decode as invoiceDecode } from "light-bolt11-decoder";
import { useMemo } from "react";
-import moment from "moment";
+import NoteTime from "./NoteTime";
export default function Invoice(props) {
const invoice = props.invoice;
@@ -48,7 +48,7 @@ export default function Invoice(props) {
{header()}
- {info?.expire ? {info?.expired ? "Expired" : "Expires"} {moment(info.expire * 1000).fromNow()} : null}
+ {info?.expire ? {info?.expired ? "Expired" : "Expires"} : null}
{info?.expired ?
Expired
:
diff --git a/src/element/Note.css b/src/element/Note.css
index e763b1f9..888ff670 100644
--- a/src/element/Note.css
+++ b/src/element/Note.css
@@ -13,10 +13,13 @@
.note > .header .reply {
font-size: small;
+ color: #999;
}
.note > .header > .info {
font-size: small;
+ white-space: nowrap;
+ color: #999;
}
.note > .body {
diff --git a/src/element/Note.js b/src/element/Note.js
index 16c0f642..7ca61a58 100644
--- a/src/element/Note.js
+++ b/src/element/Note.js
@@ -1,14 +1,14 @@
import "./Note.css";
import { useCallback } from "react";
import { useSelector } from "react-redux";
-import moment from "moment";
import { useNavigate } from "react-router-dom";
import Event from "../nostr/Event";
import ProfileImage from "./ProfileImage";
import { extractLinks, extractMentions, extractInvoices } from "../Text";
-import { eventLink } from "../Util";
+import { eventLink, hexToBech32 } from "../Util";
import NoteFooter from "./NoteFooter";
+import NoteTime from "./NoteTime";
export default function Note(props) {
const navigate = useNavigate();
@@ -57,11 +57,14 @@ export default function Note(props) {
return null;
}
+ const maxMentions = 2;
let replyId = ev.Thread?.ReplyTo?.Event;
- let mentions = ev.Thread?.PubKeys?.map(a => [a, users[a]])?.map(a => a[1]?.name ?? a[0].substring(0, 8));
+ let mentions = ev.Thread?.PubKeys?.map(a => [a, users[a]])?.map(a => a[1]?.name ?? hexToBech32("npub", a[0]).substring(0, 12))
+ .sort((a, b) => a.startsWith("npub") ? 1 : -1);
+ let pubMentions = mentions.length > maxMentions ? `${mentions?.slice(0, maxMentions).join(", ")} & ${mentions.length - maxMentions} others` : mentions?.join(", ");
return (
-
goToEvent(e, replyId)}>
- ➡️ {mentions?.join(", ") ?? replyId?.substring(0, 8)}
+
+ ➡️ {pubMentions ?? hexToBech32("note", replyId).substring(0, 12)}
)
}
@@ -84,7 +87,7 @@ export default function Note(props) {
{options.showTime ?
- {moment(ev.CreatedAt * 1000).fromNow()}
+
: null}
: null}
goToEvent(e, ev.Id)}>
diff --git a/src/element/NoteReaction.js b/src/element/NoteReaction.js
index 0f2538f8..38a4dd9d 100644
--- a/src/element/NoteReaction.js
+++ b/src/element/NoteReaction.js
@@ -1,12 +1,12 @@
import "./NoteReaction.css";
-import moment from "moment";
import EventKind from "../nostr/EventKind";
import Note from "./Note";
import ProfileImage from "./ProfileImage";
import Event from "../nostr/Event";
-import { eventLink } from "../Util";
+import { eventLink, hexToBech32 } from "../Util";
import { Link } from "react-router-dom";
import { useMemo } from "react";
+import NoteTime from "./NoteTime";
export default function NoteReaction(props) {
const ev = props["data-ev"] || Event.FromObject(props.data);
@@ -68,12 +68,12 @@ export default function NoteReaction(props) {
- {moment(ev.CreatedAt * 1000).fromNow()}
+
{root ?
: null}
- {!root && refEvent ?
#{refEvent.substring(0, 8)}
: null}
+ {!root && refEvent ?
#{hexToBech32("note", refEvent).substring(0, 12)}
: null}
);
}
\ No newline at end of file
diff --git a/src/element/NoteTime.js b/src/element/NoteTime.js
new file mode 100644
index 00000000..b260f387
--- /dev/null
+++ b/src/element/NoteTime.js
@@ -0,0 +1,40 @@
+import { useEffect, useState } from "react";
+
+const MinuteInMs = 1_000 * 60;
+const HourInMs = MinuteInMs * 60;
+const DayInMs = HourInMs * 24;
+
+export default function NoteTime(props) {
+ const from = props.from;
+ const [time, setTime] = useState("");
+
+ function calcTime() {
+ let fromDate = new Date(from);
+ let ago = (new Date().getTime()) - from;
+ let absAgo = Math.abs(ago);
+ if (absAgo > DayInMs) {
+ return fromDate.toLocaleDateString(undefined, { year: "2-digit", month: "short", day: "2-digit", weekday: "short" });
+ } else if (absAgo > HourInMs) {
+ return `${fromDate.getHours().toString().padStart(2, '0')}:${fromDate.getMinutes().toString().padStart(2, '0')}`;
+ } else {
+ let mins = parseInt(absAgo / MinuteInMs);
+ return `${mins} mins ago`;
+ }
+ }
+
+ useEffect(() => {
+ setTime(calcTime());
+ let t = setInterval(() => {
+ setTime(s => {
+ let newTime = calcTime();
+ if (newTime !== s) {
+ return newTime;
+ }
+ return s;
+ })
+ }, MinuteInMs);
+ return () => clearInterval(t);
+ }, [from]);
+
+ return <>{time}>
+}
\ No newline at end of file
diff --git a/src/element/ProfileImage.css b/src/element/ProfileImage.css
index 90db64ce..4d5f385a 100644
--- a/src/element/ProfileImage.css
+++ b/src/element/ProfileImage.css
@@ -6,7 +6,7 @@
.pfp > img {
width: 40px;
height: 40px;
- margin-right: 20px;
+ margin-right: 10px;
border-radius: 10px;
cursor: pointer;
}
diff --git a/yarn.lock b/yarn.lock
index ddbc3c1d..06242d49 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6037,11 +6037,6 @@ mkdirp@~0.5.1:
dependencies:
minimist "^1.2.6"
-moment@^2.29.4:
- version "2.29.4"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
- integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
-
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"