mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-29 11:12:12 +01:00
add relay icons
set window title on certain pages
This commit is contained in:
parent
945579d2f8
commit
a517df5386
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
18
src/components/relay-favicon.tsx
Normal file
18
src/components/relay-favicon.tsx
Normal 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";
|
@ -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;
|
||||
|
13
src/hooks/use-app-title.ts
Normal file
13
src/hooks/use-app-title.ts
Normal 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]);
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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") ?? "";
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user