mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
add relay selection to global feed
This commit is contained in:
parent
14851eaafe
commit
2fda4b2e9e
@ -29,13 +29,7 @@ export class NostrSubscription {
|
||||
|
||||
this.relays = relayUrls.map((url) => relayPool.requestRelay(url));
|
||||
}
|
||||
handleOpen(relay: Relay) {
|
||||
if (this.query) {
|
||||
// when the relay connects send the req event
|
||||
relay.send(["REQ", this.id, this.query]);
|
||||
}
|
||||
}
|
||||
handleEvent(event: IncomingEvent) {
|
||||
private handleEvent(event: IncomingEvent) {
|
||||
if (this.state === NostrSubscription.OPEN && event.subId === this.id && !this.seenEvents.has(event.body.id)) {
|
||||
this.onEvent.next(event.body);
|
||||
this.seenEvents.add(event.body.id);
|
||||
@ -47,12 +41,13 @@ export class NostrSubscription {
|
||||
}
|
||||
}
|
||||
|
||||
cleanup: SubscriptionLike[] = [];
|
||||
private cleanup = new Map<Relay, SubscriptionLike>();
|
||||
/** listen for event and open events from relays */
|
||||
private subscribeToRelays() {
|
||||
for (const relay of this.relays) {
|
||||
this.cleanup.push(relay.onEvent.subscribe(this.handleEvent.bind(this)));
|
||||
this.cleanup.push(relay.onOpen.subscribe(this.handleOpen.bind(this)));
|
||||
if (!this.cleanup.has(relay)) {
|
||||
this.cleanup.set(relay, relay.onEvent.subscribe(this.handleEvent.bind(this)));
|
||||
}
|
||||
}
|
||||
|
||||
for (const url of this.relayUrls) {
|
||||
@ -60,8 +55,9 @@ export class NostrSubscription {
|
||||
}
|
||||
}
|
||||
/** listen for event and open events from relays */
|
||||
private unsubscribeToRelays() {
|
||||
private unsubscribeFromRelays() {
|
||||
this.cleanup.forEach((sub) => sub.unsubscribe());
|
||||
this.cleanup.clear();
|
||||
|
||||
for (const url of this.relayUrls) {
|
||||
relayPool.removeClaim(url, this);
|
||||
@ -83,7 +79,7 @@ export class NostrSubscription {
|
||||
|
||||
return this;
|
||||
}
|
||||
update(query: NostrQuery) {
|
||||
setQuery(query: NostrQuery) {
|
||||
this.query = query;
|
||||
if (this.state === NostrSubscription.OPEN) {
|
||||
this.send(["REQ", this.id, this.query]);
|
||||
@ -91,13 +87,35 @@ export class NostrSubscription {
|
||||
return this;
|
||||
}
|
||||
setRelays(relays: string[]) {
|
||||
this.unsubscribeToRelays();
|
||||
this.unsubscribeFromRelays();
|
||||
const newRelays = relays.map((url) => relayPool.requestRelay(url));
|
||||
|
||||
// get new relays
|
||||
for (const relay of this.relays) {
|
||||
if (!newRelays.includes(relay)) {
|
||||
// if the subscription is open and the relay is connected
|
||||
if (this.state === NostrSubscription.OPEN && relay.connected) {
|
||||
// close the connection to this relay
|
||||
relay.send(["CLOSE", this.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const relay of newRelays) {
|
||||
if (!this.relays.includes(relay)) {
|
||||
// if the subscription is open and it has a query
|
||||
if (this.state === NostrSubscription.OPEN && this.query) {
|
||||
// open a connection to this relay
|
||||
relay.send(["REQ", this.id, this.query]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set new relays
|
||||
this.relayUrls = relays;
|
||||
this.relays = relays.map((url) => relayPool.requestRelay(url));
|
||||
this.relays = newRelays;
|
||||
|
||||
this.subscribeToRelays();
|
||||
if (this.state === NostrSubscription.OPEN) {
|
||||
this.subscribeToRelays();
|
||||
}
|
||||
}
|
||||
close() {
|
||||
if (this.state !== NostrSubscription.OPEN) return this;
|
||||
@ -109,7 +127,7 @@ export class NostrSubscription {
|
||||
// forget all seen events
|
||||
this.seenEvents.clear();
|
||||
// unsubscribe from relay messages
|
||||
this.unsubscribeToRelays();
|
||||
this.unsubscribeFromRelays();
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.info(`Subscription: "${this.name || this.id}" closed`);
|
||||
@ -117,4 +135,8 @@ export class NostrSubscription {
|
||||
|
||||
return this;
|
||||
}
|
||||
forgetEvents() {
|
||||
// forget all seen events
|
||||
this.seenEvents.clear();
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ export class ThreadLoader {
|
||||
|
||||
private updateSubscription() {
|
||||
if (this.rootId.value) {
|
||||
this.subscription.update({ "#e": [this.rootId.value], kinds: [1] });
|
||||
this.subscription.setQuery({ "#e": [this.rootId.value], kinds: [1] });
|
||||
if (this.subscription.state !== NostrSubscription.OPEN) {
|
||||
this.subscription.open();
|
||||
}
|
||||
|
@ -39,7 +39,12 @@ export class TimelineLoader {
|
||||
if (!query.since) throw new Error('Timeline requires "since" to be set in query');
|
||||
|
||||
this.query = query;
|
||||
this.subscription.update(query);
|
||||
this.subscription.setQuery(query);
|
||||
}
|
||||
|
||||
setRelays(relays: string[]) {
|
||||
this.relays = relays;
|
||||
this.subscription.setRelays(relays);
|
||||
}
|
||||
|
||||
private handleEvent(event: NostrEvent) {
|
||||
@ -78,9 +83,10 @@ export class TimelineLoader {
|
||||
this.page.next(this.page.value + 1);
|
||||
}
|
||||
|
||||
reset() {
|
||||
forgetEvents() {
|
||||
this.events.next([]);
|
||||
this.seenEvents.clear();
|
||||
this.subscription.forgetEvents();
|
||||
}
|
||||
open() {
|
||||
this.subscription.open();
|
||||
|
24
src/components/relay-url-input.tsx
Normal file
24
src/components/relay-url-input.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { Input, InputProps } from "@chakra-ui/react";
|
||||
import { useAsync } from "react-use";
|
||||
|
||||
export type RelayUrlInputProps = Omit<InputProps, "type">;
|
||||
|
||||
export const RelayUrlInput = ({ ...props }: RelayUrlInputProps) => {
|
||||
const { value: relaysJson, loading: loadingRelaysJson } = useAsync(async () =>
|
||||
fetch("/relays.json").then((res) => res.json() as Promise<{ relays: string[] }>)
|
||||
);
|
||||
const relaySuggestions = relaysJson?.relays ?? [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input list="relay-suggestions" type="url" isDisabled={props.isDisabled ?? loadingRelaysJson} {...props} />
|
||||
<datalist id="relay-suggestions">
|
||||
{relaySuggestions.map((url) => (
|
||||
<option key={url} value={url}>
|
||||
{url}
|
||||
</option>
|
||||
))}
|
||||
</datalist>
|
||||
</>
|
||||
);
|
||||
};
|
@ -17,7 +17,7 @@ export function useSubscription(query: NostrQuery, opts?: Options) {
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
if (sub.current) {
|
||||
sub.current.update(query);
|
||||
sub.current.setQuery(query);
|
||||
if (opts?.enabled ?? true) sub.current.open();
|
||||
else sub.current.close();
|
||||
}
|
||||
|
@ -1,25 +1,27 @@
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useUnmount } from "react-use";
|
||||
import { useDeepCompareEffect, useUnmount } from "react-use";
|
||||
import { NostrQueryWithStart, TimelineLoader, TimelineLoaderOptions } from "../classes/timeline-loader";
|
||||
import settings from "../services/settings";
|
||||
import useSubject from "./use-subject";
|
||||
|
||||
type Options = TimelineLoaderOptions & {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export function useTimelineLoader(key: string, query: NostrQueryWithStart, opts?: Options) {
|
||||
const relays = useSubject(settings.relays);
|
||||
export function useTimelineLoader(key: string, relays: string[], query: NostrQueryWithStart, opts?: Options) {
|
||||
if (opts && !opts.name) opts.name = key;
|
||||
|
||||
const ref = useRef<TimelineLoader | null>(null);
|
||||
const loader = (ref.current = ref.current || new TimelineLoader(relays, query, opts));
|
||||
|
||||
useEffect(() => {
|
||||
loader.reset();
|
||||
loader.forgetEvents();
|
||||
loader.setQuery(query);
|
||||
}, [key]);
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
loader.setRelays(relays);
|
||||
}, [relays]);
|
||||
|
||||
const enabled = opts?.enabled ?? true;
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
|
@ -71,7 +71,7 @@ function flushRequests() {
|
||||
const query: NostrQuery = { authors: Array.from(pubkeys), kinds: [3] };
|
||||
|
||||
subscription.setRelays(Array.from(relays));
|
||||
subscription.update(query);
|
||||
subscription.setQuery(query);
|
||||
if (subscription.state !== NostrSubscription.OPEN) {
|
||||
subscription.open();
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ function flushRequests() {
|
||||
const query: NostrQuery = { kinds: [3], "#p": Array.from(pubkeys) };
|
||||
|
||||
subscription.setRelays(Array.from(relays));
|
||||
subscription.update(query);
|
||||
subscription.setQuery(query);
|
||||
if (subscription.state !== NostrSubscription.OPEN) {
|
||||
subscription.open();
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ function flushRequests() {
|
||||
const query: NostrQuery = { authors: Array.from(pubkeys), kinds: [0] };
|
||||
|
||||
subscription.setRelays(Array.from(relays));
|
||||
subscription.update(query);
|
||||
subscription.setQuery(query);
|
||||
if (subscription.state !== NostrSubscription.OPEN) {
|
||||
subscription.open();
|
||||
}
|
||||
|
145
src/views/home/components/feed-filters.tsx
Normal file
145
src/views/home/components/feed-filters.tsx
Normal file
@ -0,0 +1,145 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
ModalProps,
|
||||
Switch,
|
||||
} from "@chakra-ui/react";
|
||||
import { useState } from "react";
|
||||
import { useList } from "react-use";
|
||||
import { RelayUrlInput } from "../../../components/relay-url-input";
|
||||
import { unique } from "../../../helpers/array";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
import settings from "../../../services/settings";
|
||||
|
||||
const CustomRelayForm = ({ onSubmit }: { onSubmit: (url: string) => void }) => {
|
||||
const [customRelay, setCustomRelay] = useState("");
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onSubmit(customRelay);
|
||||
setCustomRelay("");
|
||||
}}
|
||||
>
|
||||
<Flex gap="2">
|
||||
<RelayUrlInput
|
||||
size="sm"
|
||||
placeholder="wss://relay.example.com"
|
||||
value={customRelay}
|
||||
onChange={(e) => setCustomRelay(e.target.value)}
|
||||
/>
|
||||
<Button size="sm" type="submit">
|
||||
Add
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export type FilterValues = {
|
||||
relays: string[];
|
||||
};
|
||||
|
||||
export type FeedFiltersProps = {
|
||||
isOpen: boolean;
|
||||
onClose: ModalProps["onClose"];
|
||||
values: FilterValues;
|
||||
onSave: (values: FilterValues) => void;
|
||||
};
|
||||
|
||||
export const FeedFilters = ({ isOpen, onClose, values }: FeedFiltersProps) => {
|
||||
const defaultRelays = useSubject(settings.relays);
|
||||
|
||||
const [selectedRelays, relayActions] = useList(values.relays);
|
||||
const availableRelays = unique([...defaultRelays, ...selectedRelays]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} size="4xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Filters</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Accordion allowToggle>
|
||||
<AccordionItem>
|
||||
<h2>
|
||||
<AccordionButton>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
Relays
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</h2>
|
||||
<AccordionPanel pb={4}>
|
||||
{availableRelays.map((url) => (
|
||||
<Box key={url}>
|
||||
<FormLabel>
|
||||
<Switch
|
||||
size="sm"
|
||||
mr="2"
|
||||
isChecked={selectedRelays.includes(url)}
|
||||
onChange={() =>
|
||||
selectedRelays.includes(url)
|
||||
? relayActions.removeAt(selectedRelays.indexOf(url))
|
||||
: relayActions.push(url)
|
||||
}
|
||||
/>
|
||||
{url}
|
||||
</FormLabel>
|
||||
</Box>
|
||||
))}
|
||||
<Flex gap="2">
|
||||
<Button size="sm" onClick={() => relayActions.set(defaultRelays)}>
|
||||
Select All
|
||||
</Button>
|
||||
<CustomRelayForm onSubmit={(url) => relayActions.push(url)} />
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<h2>
|
||||
<AccordionButton>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
Add Custom
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</h2>
|
||||
<AccordionPanel pb={4}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||
ex ea commodo consequat.
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button mr={3} onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button colorScheme="blue" variant="solid">
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
@ -9,6 +9,7 @@ import identity from "../../services/identity";
|
||||
import userContactsService from "../../services/user-contacts";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import { isNote } from "../../helpers/nostr-event";
|
||||
import settings from "../../services/settings";
|
||||
|
||||
function useExtendedContacts(pubkey: string) {
|
||||
const [extendedContacts, setExtendedContacts] = useState<string[]>([]);
|
||||
@ -39,10 +40,12 @@ function useExtendedContacts(pubkey: string) {
|
||||
|
||||
export const DiscoverTab = () => {
|
||||
const pubkey = useSubject(identity.pubkey);
|
||||
const relays = useSubject(settings.relays);
|
||||
|
||||
const contactsOfContacts = useExtendedContacts(pubkey);
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
`discover`,
|
||||
relays,
|
||||
{ authors: contactsOfContacts, kinds: [1], since: moment().subtract(1, "hour").unix() },
|
||||
{ pageSize: moment.duration(1, "hour").asSeconds(), enabled: contactsOfContacts.length > 0 }
|
||||
);
|
||||
|
@ -7,9 +7,11 @@ import useSubject from "../../hooks/use-subject";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import { useUserContacts } from "../../hooks/use-user-contacts";
|
||||
import identity from "../../services/identity";
|
||||
import settings from "../../services/settings";
|
||||
|
||||
export const FollowingTab = () => {
|
||||
const pubkey = useSubject(identity.pubkey);
|
||||
const relays = useSubject(settings.relays);
|
||||
const contacts = useUserContacts(pubkey);
|
||||
const [search, setSearch] = useSearchParams();
|
||||
const showReplies = search.has("replies");
|
||||
@ -20,6 +22,7 @@ export const FollowingTab = () => {
|
||||
const following = contacts?.contacts || [];
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
`following-posts`,
|
||||
relays,
|
||||
{ authors: following, kinds: [1], since: moment().subtract(2, "hour").unix() },
|
||||
{ pageSize: moment.duration(2, "hour").asSeconds(), enabled: following.length > 0 }
|
||||
);
|
||||
|
@ -1,24 +1,60 @@
|
||||
import { Button, Flex, Spinner } from "@chakra-ui/react";
|
||||
import { Button, Flex, FormControl, FormLabel, Select, Spinner, Switch, useDisclosure } from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import { useState } from "react";
|
||||
import { Note } from "../../components/note";
|
||||
import { isNote } from "../../helpers/nostr-event";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import settings from "../../services/settings";
|
||||
|
||||
export const GlobalTab = () => {
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
const availableRelays = useSubject(settings.relays);
|
||||
const [selectedRelay, setSelectedRelay] = useState("");
|
||||
const { isOpen: showReplies, onToggle } = useDisclosure();
|
||||
const { events, loading, loadMore, loader } = useTimelineLoader(
|
||||
`global`,
|
||||
selectedRelay ? [selectedRelay] : availableRelays,
|
||||
{ kinds: [1], since: moment().subtract(5, "minutes").unix() },
|
||||
{ pageSize: moment.duration(5, "minutes").asSeconds() }
|
||||
);
|
||||
|
||||
const timeline = events.filter(isNote);
|
||||
const timeline = showReplies ? events : events.filter(isNote);
|
||||
|
||||
return (
|
||||
<Flex direction="column" overflow="auto" gap="2">
|
||||
{timeline.map((event) => (
|
||||
<Note key={event.id} event={event} />
|
||||
))}
|
||||
{loading ? <Spinner ml="auto" mr="auto" mt="8" mb="8" /> : <Button onClick={() => loadMore()}>Load More</Button>}
|
||||
</Flex>
|
||||
<>
|
||||
<Flex direction="column" overflow="auto" gap="2">
|
||||
<Flex gap="2">
|
||||
<Select
|
||||
placeholder="All Relays"
|
||||
maxWidth="250"
|
||||
value={selectedRelay}
|
||||
onChange={(e) => {
|
||||
setSelectedRelay(e.target.value);
|
||||
loader.forgetEvents();
|
||||
}}
|
||||
>
|
||||
{availableRelays.map((url) => (
|
||||
<option key={url} value={url}>
|
||||
{url}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<FormControl display="flex" alignItems="center">
|
||||
<Switch id="show-replies" isChecked={showReplies} onChange={onToggle} mr="2" />
|
||||
<FormLabel htmlFor="show-replies" mb="0">
|
||||
Show Replies
|
||||
</FormLabel>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
{timeline.map((event) => (
|
||||
<Note key={event.id} event={event} />
|
||||
))}
|
||||
{loading ? (
|
||||
<Spinner ml="auto" mr="auto" mt="8" mb="8" />
|
||||
) : (
|
||||
<Button onClick={() => loadMore()}>Load More</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -28,16 +28,12 @@ import { RelayStatus } from "./relay-status";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import settings from "../../services/settings";
|
||||
import { clearData } from "../../services/db";
|
||||
import { RelayUrlInput } from "../../components/relay-url-input";
|
||||
|
||||
export const SettingsView = () => {
|
||||
const relays = useSubject(settings.relays);
|
||||
const [relayInputValue, setRelayInputValue] = useState("");
|
||||
|
||||
const { value: relaysJson, loading: loadingRelaysJson } = useAsync(async () =>
|
||||
fetch("/relays.json").then((res) => res.json() as Promise<{ relays: string[] }>)
|
||||
);
|
||||
const relaySuggestions = relaysJson?.relays.filter((url) => !relays.includes(url)) ?? [];
|
||||
|
||||
const { colorMode, setColorMode } = useColorMode();
|
||||
|
||||
const handleRemoveRelay = (url: string) => {
|
||||
@ -46,8 +42,10 @@ export const SettingsView = () => {
|
||||
const handleAddRelay = (event: SyntheticEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
settings.relays.next([...relays, relayInputValue]);
|
||||
setRelayInputValue("");
|
||||
if (!relays.includes(relayInputValue)) {
|
||||
settings.relays.next([...relays, relayInputValue]);
|
||||
setRelayInputValue("");
|
||||
}
|
||||
};
|
||||
|
||||
const [clearing, setClearing] = useState(false);
|
||||
@ -104,22 +102,12 @@ export const SettingsView = () => {
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="relay-url-input">Add Relay</FormLabel>
|
||||
<Flex gap="2">
|
||||
<Input
|
||||
<RelayUrlInput
|
||||
id="relay-url-input"
|
||||
value={relayInputValue}
|
||||
onChange={(e) => setRelayInputValue(e.target.value)}
|
||||
required
|
||||
list="relay-suggestions"
|
||||
type="url"
|
||||
isDisabled={loadingRelaysJson}
|
||||
isRequired
|
||||
/>
|
||||
<datalist id="relay-suggestions">
|
||||
{relaySuggestions.map((url) => (
|
||||
<option key={url} value={url}>
|
||||
{url}
|
||||
</option>
|
||||
))}
|
||||
</datalist>
|
||||
<Button type="submit">Add</Button>
|
||||
</Flex>
|
||||
</FormControl>
|
||||
|
@ -3,13 +3,17 @@ import moment from "moment";
|
||||
import { useOutletContext } from "react-router-dom";
|
||||
import { Note } from "../../components/note";
|
||||
import { isNote } from "../../helpers/nostr-event";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import settings from "../../services/settings";
|
||||
|
||||
const UserNotesTab = () => {
|
||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||
const relays = useSubject(settings.relays);
|
||||
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
`${pubkey} notes`,
|
||||
relays,
|
||||
{ authors: [pubkey], kinds: [1], since: moment().subtract(1, "day").unix() },
|
||||
{ pageSize: moment.duration(1, "day").asSeconds() }
|
||||
);
|
||||
|
@ -3,12 +3,17 @@ import moment from "moment";
|
||||
import { useOutletContext } from "react-router-dom";
|
||||
import { Note } from "../../components/note";
|
||||
import { isReply } from "../../helpers/nostr-event";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import settings from "../../services/settings";
|
||||
|
||||
const UserRepliesTab = () => {
|
||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||
const relays = useSubject(settings.relays);
|
||||
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
`${pubkey} replies`,
|
||||
relays,
|
||||
{ authors: [pubkey], kinds: [1], since: moment().subtract(4, "hours").unix() },
|
||||
{ pageSize: moment.duration(1, "day").asSeconds() }
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user