mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-25 19:23:45 +02:00
add following and discover tabs to home view
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
ModalOverlay,
|
||||
} from "@chakra-ui/react";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import { PostContents } from "./post-contents";
|
||||
import { PostContents } from "./post/post-contents";
|
||||
|
||||
export type PostModalProps = {
|
||||
event: NostrEvent;
|
||||
|
@@ -20,7 +20,7 @@ import { UserAvatarLink } from "../user-avatar-link";
|
||||
import { getUserDisplayName } from "../../helpers/user-metadata";
|
||||
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
|
||||
|
||||
import { PostContents } from "../post-contents";
|
||||
import { PostContents } from "./post-contents";
|
||||
import { PostMenu } from "./post-menu";
|
||||
import { PostCC } from "./post-cc";
|
||||
|
||||
@@ -35,7 +35,7 @@ export const Post = React.memo(({ event }: PostProps) => {
|
||||
<Card padding="2" variant="outline">
|
||||
<CardHeader padding="0" mb="2">
|
||||
<HStack spacing="4">
|
||||
<Flex flex="1" gap="2" alignItems="center" flexWrap="wrap">
|
||||
<Flex flex="1" gap="2">
|
||||
<UserAvatarLink pubkey={event.pubkey} size="sm" />
|
||||
|
||||
<Box>
|
||||
|
@@ -23,7 +23,7 @@ export const PostCC = ({ event }: { event: NostrEvent }) => {
|
||||
if (!hasCC) return null;
|
||||
|
||||
return (
|
||||
<Text fontSize="sm">
|
||||
<Text fontSize="sm" color="gray.500">
|
||||
<span>Replying to: </span>
|
||||
{event.tags
|
||||
.filter((t) => t[0] === "p")
|
||||
|
@@ -13,8 +13,8 @@ import remarkUnwrapImages from "remark-unwrap-images";
|
||||
import rehypeExternalLinks from "rehype-external-links";
|
||||
// @ts-ignore
|
||||
import linkifyRegex from "remark-linkify-regex";
|
||||
import { InlineInvoiceCard } from "./inline-invoice-card";
|
||||
import { TweetEmbed } from "./tweet-embed";
|
||||
import { InlineInvoiceCard } from "../inline-invoice-card";
|
||||
import { TweetEmbed } from "../tweet-embed";
|
||||
|
||||
const lightningInvoiceRegExp = /(lightning:)?LNBC[A-Za-z0-9]+/i;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import moment from "moment";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
import { Providers } from "./providers";
|
||||
@@ -11,3 +12,7 @@ root.render(
|
||||
<App />
|
||||
</Providers>
|
||||
);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
window.moment = moment;
|
||||
}
|
||||
|
@@ -75,10 +75,10 @@ export const GlobalView = () => {
|
||||
flexDirection="column"
|
||||
flexGrow="1"
|
||||
overflow="hidden"
|
||||
isManual
|
||||
isLazy
|
||||
>
|
||||
<TabList>
|
||||
<Tab>Posts</Tab>
|
||||
<Tab>Notes</Tab>
|
||||
<Tab>Replies</Tab>
|
||||
</TabList>
|
||||
<TabPanels overflow="auto" height="100%">
|
||||
|
@@ -1,28 +0,0 @@
|
||||
import { HStack } from "@chakra-ui/react";
|
||||
import { UserAvatarLink } from "../components/user-avatar-link";
|
||||
import { normalizeToHex } from "../helpers/nip-19";
|
||||
|
||||
export const HomeView = () => {
|
||||
return (
|
||||
<HStack spacing=".5rem">
|
||||
<UserAvatarLink pubkey="32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" />
|
||||
<UserAvatarLink
|
||||
pubkey={
|
||||
normalizeToHex(
|
||||
"npub1dergggklka99wwrs92yz8wdjs952h2ux2ha2ed598ngwu9w7a6fsh9xzpc"
|
||||
) ?? ""
|
||||
}
|
||||
/>
|
||||
<UserAvatarLink pubkey="00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700" />
|
||||
<UserAvatarLink pubkey="82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2" />
|
||||
<UserAvatarLink pubkey="85080d3bad70ccdcd7f74c29a44f55bb85cbcd3dd0cbb957da1d215bdb931204" />
|
||||
<UserAvatarLink pubkey="e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411" />
|
||||
<UserAvatarLink pubkey="8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168" />
|
||||
<UserAvatarLink pubkey="c4eabae1be3cf657bc1855ee05e69de9f059cb7a059227168b80b89761cbc4e0" />
|
||||
<UserAvatarLink pubkey="e33fe65f1fde44c6dc17eeb38fdad0fceaf1cae8722084332ed1e32496291d42" />
|
||||
<UserAvatarLink pubkey="c5cfda98d01f152b3493d995eed4cdb4d9e55a973925f6f9ea24769a5a21e778" />
|
||||
<UserAvatarLink pubkey="3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d" />
|
||||
<UserAvatarLink pubkey="04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9" />
|
||||
</HStack>
|
||||
);
|
||||
};
|
77
src/views/home/discover-tab.tsx
Normal file
77
src/views/home/discover-tab.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Flex, Text } from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import { mergeAll, from } from "rxjs";
|
||||
import { Post } from "../../components/post";
|
||||
import { useEventDir } from "../../hooks/use-event-dir";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useSubscription } from "../../hooks/use-subscription";
|
||||
import { useUserContacts } from "../../hooks/use-user-contacts";
|
||||
import identity from "../../services/identity";
|
||||
import settings from "../../services/settings";
|
||||
import userContacts from "../../services/user-contacts";
|
||||
|
||||
function useExtendedContacts(pubkey: string) {
|
||||
const relays = useSubject(settings.relays);
|
||||
const [extendedContacts, setExtendedContacts] = useState<string[]>([]);
|
||||
const { contacts } = useUserContacts(pubkey);
|
||||
|
||||
useEffect(() => {
|
||||
if (contacts) {
|
||||
const following = contacts.contacts.map((c) => c.pubkey);
|
||||
const subscriptions = contacts.contacts.map((contact) =>
|
||||
userContacts.requestUserContacts(contact.pubkey, relays)
|
||||
);
|
||||
|
||||
const rxSub = from(subscriptions)
|
||||
.pipe(mergeAll())
|
||||
.subscribe((contacts) => {
|
||||
if (contacts) {
|
||||
setExtendedContacts((value) => {
|
||||
const more = contacts.contacts
|
||||
.map((c) => c.pubkey)
|
||||
.filter((key) => !following.includes(key));
|
||||
return Array.from(new Set([...value, ...more]));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => rxSub.unsubscribe();
|
||||
}
|
||||
}, [contacts, setExtendedContacts]);
|
||||
|
||||
return extendedContacts;
|
||||
}
|
||||
|
||||
export const DiscoverTab = () => {
|
||||
const relays = useSubject(settings.relays);
|
||||
const pubkey = useSubject(identity.pubkey);
|
||||
|
||||
const contactsOfContacts = useExtendedContacts(pubkey);
|
||||
|
||||
const [since, setSince] = useState(moment().subtract(1, "hour"));
|
||||
const [after, setAfter] = useState(moment());
|
||||
|
||||
const sub = useSubscription(
|
||||
relays,
|
||||
{
|
||||
authors: contactsOfContacts,
|
||||
kinds: [1],
|
||||
since: since.unix(),
|
||||
},
|
||||
"home-discover"
|
||||
);
|
||||
|
||||
const { events } = useEventDir(sub);
|
||||
const timeline = Object.values(events).sort(
|
||||
(a, b) => b.created_at - a.created_at
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex direction="column" overflow="auto" gap="2">
|
||||
{timeline.map((event) => (
|
||||
<Post key={event.id} event={event} />
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
};
|
43
src/views/home/following-tab.tsx
Normal file
43
src/views/home/following-tab.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import { useState } from "react";
|
||||
import { Post } from "../../components/post";
|
||||
import { useEventDir } from "../../hooks/use-event-dir";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useSubscription } from "../../hooks/use-subscription";
|
||||
import { useUserContacts } from "../../hooks/use-user-contacts";
|
||||
import identity from "../../services/identity";
|
||||
import settings from "../../services/settings";
|
||||
|
||||
export const FollowingTab = () => {
|
||||
const relays = useSubject(settings.relays);
|
||||
const pubkey = useSubject(identity.pubkey);
|
||||
const { contacts } = useUserContacts(pubkey);
|
||||
|
||||
const [since, setSince] = useState(moment().subtract(1, "hour"));
|
||||
const [after, setAfter] = useState(moment());
|
||||
|
||||
const following = contacts?.contacts.map((contact) => contact.pubkey) || [];
|
||||
const sub = useSubscription(
|
||||
relays,
|
||||
{
|
||||
authors: following,
|
||||
kinds: [1],
|
||||
since: since.unix(),
|
||||
},
|
||||
"home-following"
|
||||
);
|
||||
|
||||
const { events } = useEventDir(sub);
|
||||
const timeline = Object.values(events).sort(
|
||||
(a, b) => b.created_at - a.created_at
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex direction="column" overflow="auto" gap="2">
|
||||
{timeline.map((event) => (
|
||||
<Post key={event.id} event={event} />
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
};
|
28
src/views/home/index.tsx
Normal file
28
src/views/home/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
|
||||
import { DiscoverTab } from "./discover-tab";
|
||||
import { FollowingTab } from "./following-tab";
|
||||
|
||||
export const HomeView = () => {
|
||||
return (
|
||||
<Tabs
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
flexGrow="1"
|
||||
overflow="hidden"
|
||||
isLazy
|
||||
>
|
||||
<TabList>
|
||||
<Tab>Following</Tab>
|
||||
<Tab>Discover</Tab>
|
||||
</TabList>
|
||||
<TabPanels overflow="auto" height="100%">
|
||||
<TabPanel pr={0} pl={0}>
|
||||
<FollowingTab />
|
||||
</TabPanel>
|
||||
<TabPanel pr={0} pl={0}>
|
||||
<DiscoverTab />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
@@ -85,10 +85,10 @@ export const UserView = ({ pubkey }: UserViewProps) => {
|
||||
flexDirection="column"
|
||||
flexGrow="1"
|
||||
overflow="hidden"
|
||||
isManual
|
||||
isLazy
|
||||
>
|
||||
<TabList>
|
||||
<Tab>Posts</Tab>
|
||||
<Tab>Notes</Tab>
|
||||
{/* <Tab>Replies</Tab> */}
|
||||
<Tab>Following</Tab>
|
||||
<Tab>Relays</Tab>
|
||||
|
Reference in New Issue
Block a user