diff --git a/README.md b/README.md
index 8a27743bc..01114470c 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,6 @@
- Add preview tab to note modal
- Save note drafts and let users manage them
- Add support for relay favicons
-- Setup react-helmet to update window title
## Setup
diff --git a/src/components/note/index.tsx b/src/components/note/index.tsx
index 770c2af6c..86030ff78 100644
--- a/src/components/note/index.tsx
+++ b/src/components/note/index.tsx
@@ -20,6 +20,7 @@ import { PostModalContext } from "../../providers/post-modal-provider";
import { buildReply } from "../../helpers/nostr-event";
import { UserDnsIdentityIcon } from "../user-dns-identity";
import { useReadonlyMode } from "../../hooks/use-readonly-mode";
+import { convertTimestampToDate } from "../../helpers/date";
export type NoteProps = {
event: NostrEvent;
@@ -48,7 +49,7 @@ export const Note = React.memo(({ event, maxHeight }: NoteProps) => {
{!isMobile && }
- {moment(event.created_at * 1000).fromNow()}
+ {moment(convertTimestampToDate(event.created_at)).fromNow()}
diff --git a/src/components/note/note-relays.tsx b/src/components/note/note-relays.tsx
index 0410105bf..c5c15ec05 100644
--- a/src/components/note/note-relays.tsx
+++ b/src/components/note/note-relays.tsx
@@ -20,6 +20,7 @@ import { relayPool } from "../../services/relays";
import settings from "../../services/settings";
import { NostrEvent } from "../../types/nostr-event";
import { RelayIcon, SearchIcon } from "../icons";
+import { RelayFavicon } from "../relay-favicon";
export type NoteRelaysProps = Omit & {
event: NostrEvent;
@@ -71,7 +72,10 @@ export const NoteRelays = memo(({ event, ...props }: NoteRelaysProps) => {
{relays.map((url) => (
- {url}
+
+
+ {url}
+
))}
diff --git a/src/components/relay-favicon.tsx b/src/components/relay-favicon.tsx
new file mode 100644
index 000000000..73a83e8fd
--- /dev/null
+++ b/src/components/relay-favicon.tsx
@@ -0,0 +1,18 @@
+import React, { useMemo } from "react";
+import { Avatar, AvatarProps } from "@chakra-ui/react";
+import { RelayIcon } from "./icons";
+
+export type RelayFaviconProps = Omit & {
+ relay: string;
+};
+export const RelayFavicon = React.memo(({ relay, ...props }: RelayFaviconProps) => {
+ const url = useMemo(() => {
+ const url = new URL(relay);
+ url.protocol = "https:";
+ url.pathname = "/favicon.ico";
+ return url.toString();
+ }, [relay]);
+
+ return } overflow="hidden" {...props} />;
+});
+RelayFavicon.displayName = "RelayFavicon";
diff --git a/src/helpers/thread.ts b/src/helpers/thread.ts
index 311dfa4bc..435f04ce7 100644
--- a/src/helpers/thread.ts
+++ b/src/helpers/thread.ts
@@ -38,6 +38,8 @@ export function linkEvents(events: NostrEvent[]) {
reply.reply = reply.refs.replyId ? replies.get(reply.refs.replyId) : undefined;
reply.replies = idToChildren[id]?.map((e) => replies.get(e.id) as ThreadItem) ?? [];
+
+ reply.replies.sort((a, b) => a.event.created_at - b.event.created_at);
}
return replies;
diff --git a/src/hooks/use-app-title.ts b/src/hooks/use-app-title.ts
new file mode 100644
index 000000000..f911fb800
--- /dev/null
+++ b/src/hooks/use-app-title.ts
@@ -0,0 +1,13 @@
+import { useEffect } from "react";
+
+const appName = "noStrudel";
+
+export function useAppTitle(title?: string) {
+ useEffect(() => {
+ document.title = [title, appName].filter(Boolean).join(" | ");
+
+ return () => {
+ document.title = appName;
+ };
+ }, [title]);
+}
diff --git a/src/views/home/discover-tab.tsx b/src/views/home/discover-tab.tsx
index 9bc34983b..eefa80997 100644
--- a/src/views/home/discover-tab.tsx
+++ b/src/views/home/discover-tab.tsx
@@ -10,8 +10,10 @@ import userContactsService from "../../services/user-contacts";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import { isNote } from "../../helpers/nostr-event";
import settings from "../../services/settings";
+import { useAppTitle } from "../../hooks/use-app-title";
function useExtendedContacts(pubkey: string) {
+ useAppTitle("discover");
const [extendedContacts, setExtendedContacts] = useState([]);
const contacts = useUserContacts(pubkey);
diff --git a/src/views/home/global-tab.tsx b/src/views/home/global-tab.tsx
index 5d7fa9e83..8f9d27e4a 100644
--- a/src/views/home/global-tab.tsx
+++ b/src/views/home/global-tab.tsx
@@ -4,11 +4,13 @@ import { useSearchParams } from "react-router-dom";
import { Note } from "../../components/note";
import { unique } from "../../helpers/array";
import { isNote } from "../../helpers/nostr-event";
+import { useAppTitle } from "../../hooks/use-app-title";
import useSubject from "../../hooks/use-subject";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import settings from "../../services/settings";
export const GlobalTab = () => {
+ useAppTitle("global");
const defaultRelays = useSubject(settings.relays);
const [searchParams, setSearchParams] = useSearchParams();
const selectedRelay = searchParams.get("relay") ?? "";
diff --git a/src/views/settings/index.tsx b/src/views/settings/index.tsx
index 34f205498..5e4ec0c64 100644
--- a/src/views/settings/index.tsx
+++ b/src/views/settings/index.tsx
@@ -3,7 +3,6 @@ import {
Flex,
FormControl,
FormLabel,
- Input,
Switch,
useColorMode,
Table,
@@ -22,9 +21,10 @@ import {
AccordionIcon,
ButtonGroup,
FormHelperText,
+ Text,
} from "@chakra-ui/react";
import { SyntheticEvent, useState } from "react";
-import { GlobalIcon, TrashIcon } from "../../components/icons";
+import { GlobalIcon, RelayIcon, TrashIcon } from "../../components/icons";
import { RelayStatus } from "./relay-status";
import useSubject from "../../hooks/use-subject";
import settings from "../../services/settings";
@@ -32,6 +32,7 @@ import { clearCacheData, deleteDatabase } from "../../services/db";
import { RelayUrlInput } from "../../components/relay-url-input";
import { useNavigate } from "react-router-dom";
import identity from "../../services/identity";
+import { RelayFavicon } from "../../components/relay-favicon";
export const SettingsView = () => {
const navigate = useNavigate();
@@ -94,7 +95,12 @@ export const SettingsView = () => {
{relays.map((url) => (
- {url} |
+
+
+
+ {url}
+
+ |
|
diff --git a/src/views/user/index.tsx b/src/views/user/index.tsx
index 8f50c370f..aac4bb68e 100644
--- a/src/views/user/index.tsx
+++ b/src/views/user/index.tsx
@@ -10,8 +10,6 @@ import {
Text,
Link,
IconButton,
- ButtonGroup,
- Button,
} from "@chakra-ui/react";
import { Outlet, useLoaderData, useMatches, useNavigate } from "react-router-dom";
import { useUserMetadata } from "../../hooks/use-user-metadata";
@@ -28,6 +26,7 @@ import { KeyIcon, SettingsIcon } from "../../components/icons";
import { CopyIconButton } from "../../components/copy-icon-button";
import identity from "../../services/identity";
import { UserFollowButton } from "../../components/user-follow-button";
+import { useAppTitle } from "../../hooks/use-app-title";
const tabs = [
{ label: "Notes", path: "notes" },
@@ -51,6 +50,8 @@ const UserView = () => {
const npub = normalizeToBech32(pubkey, Bech32Prefix.Pubkey);
const isSelf = pubkey === identity.pubkey.value;
+ useAppTitle(getUserDisplayName(metadata, npub ?? pubkey));
+
const header = (