add relay icons

set window title on certain pages
This commit is contained in:
hzrd149 2023-02-09 08:02:27 -06:00
parent 945579d2f8
commit a517df5386
10 changed files with 56 additions and 8 deletions

View File

@ -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

View File

@ -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) => {
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
{!isMobile && <Flex grow={1} />}
<Link as={RouterLink} to={`/n/${normalizeToBech32(event.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
{moment(event.created_at * 1000).fromNow()}
{moment(convertTimestampToDate(event.created_at)).fromNow()}
</Link>
</Flex>
</CardHeader>

View File

@ -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<IconButtonProps, "icon" | "aria-label"> & {
event: NostrEvent;
@ -71,7 +72,10 @@ export const NoteRelays = memo(({ event, ...props }: NoteRelaysProps) => {
<PopoverArrow />
<PopoverBody>
{relays.map((url) => (
<Text key={url}>{url}</Text>
<Flex alignItems="center" key={url}>
<RelayFavicon relay={url} size="2xs" mr="2" />
<Text>{url}</Text>
</Flex>
))}
</PopoverBody>
<PopoverFooter>

View File

@ -0,0 +1,18 @@
import React, { useMemo } from "react";
import { Avatar, AvatarProps } from "@chakra-ui/react";
import { RelayIcon } from "./icons";
export type RelayFaviconProps = Omit<AvatarProps, "src"> & {
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 <Avatar src={url} icon={<RelayIcon />} overflow="hidden" {...props} />;
});
RelayFavicon.displayName = "RelayFavicon";

View File

@ -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;

View File

@ -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]);
}

View File

@ -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<string[]>([]);
const contacts = useUserContacts(pubkey);

View File

@ -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") ?? "";

View File

@ -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 = () => {
<Tbody>
{relays.map((url) => (
<Tr key={url}>
<Td>{url}</Td>
<Td>
<Flex alignItems="center">
<RelayFavicon size="xs" relay={url} mr="2" />
<Text>{url}</Text>
</Flex>
</Td>
<Td>
<RelayStatus url={url} />
</Td>

View File

@ -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 = (
<Flex direction="column" gap="2" px="2" pt="2">
<Flex gap="4">