2023-09-05 13:57:50 +00:00
|
|
|
import "./Timeline.css";
|
2024-01-04 17:01:18 +00:00
|
|
|
|
2024-01-08 09:24:14 +00:00
|
|
|
import { EventKind, NostrEvent, TaggedNostrEvent } from "@snort/system";
|
2024-01-17 15:47:01 +00:00
|
|
|
import { ReactNode, useCallback, useMemo, useState } from "react";
|
2024-01-04 17:01:18 +00:00
|
|
|
import { Link } from "react-router-dom";
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2024-01-04 13:48:19 +00:00
|
|
|
import { DisplayAs, DisplayAsSelector } from "@/Components/Feed/DisplayAsSelector";
|
2024-01-04 17:01:18 +00:00
|
|
|
import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer";
|
2024-01-24 11:43:51 +00:00
|
|
|
import useTimelineFeed, { TimelineFeedOptions, TimelineSubject } from "@/Feed/TimelineFeed";
|
2024-04-15 21:31:51 +00:00
|
|
|
import useFollowsControls from "@/Hooks/useFollowControls";
|
2024-02-16 11:17:05 +00:00
|
|
|
import useHistoryState from "@/Hooks/useHistoryState";
|
2024-01-04 17:01:18 +00:00
|
|
|
import useLogin from "@/Hooks/useLogin";
|
2024-01-24 11:43:51 +00:00
|
|
|
import { dedupeByPubkey } from "@/Utils";
|
2023-09-05 13:57:50 +00:00
|
|
|
|
|
|
|
export interface TimelineFollowsProps {
|
2023-09-05 13:59:44 +00:00
|
|
|
postsOnly: boolean;
|
2023-09-07 14:54:25 +00:00
|
|
|
liveStreams?: boolean;
|
|
|
|
noteFilter?: (ev: NostrEvent) => boolean;
|
|
|
|
noteRenderer?: (ev: NostrEvent) => ReactNode;
|
2023-09-18 11:37:15 +00:00
|
|
|
noteOnClick?: (ev: NostrEvent) => void;
|
2023-11-28 19:41:53 +00:00
|
|
|
displayAs?: DisplayAs;
|
2023-11-28 20:16:00 +00:00
|
|
|
showDisplayAsSelector?: boolean;
|
2023-09-05 13:57:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of notes by "subject"
|
|
|
|
*/
|
|
|
|
const TimelineFollows = (props: TimelineFollowsProps) => {
|
2024-04-22 13:38:14 +00:00
|
|
|
const login = useLogin(s => ({
|
|
|
|
publicKey: s.publicKey,
|
|
|
|
feedDisplayAs: s.feedDisplayAs,
|
|
|
|
tags: s.state.getList(EventKind.InterestSet),
|
|
|
|
}));
|
2023-11-29 07:45:58 +00:00
|
|
|
const displayAsInitial = props.displayAs ?? login.feedDisplayAs ?? "list";
|
|
|
|
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
|
2024-02-16 11:17:05 +00:00
|
|
|
const [openedAt] = useHistoryState(Math.floor(Date.now() / 1000), "openedAt");
|
2024-04-15 21:31:51 +00:00
|
|
|
const { isFollowing, followList } = useFollowsControls();
|
2024-01-24 11:43:51 +00:00
|
|
|
const subject = useMemo(
|
|
|
|
() =>
|
|
|
|
({
|
|
|
|
type: "pubkey",
|
2024-04-15 21:31:51 +00:00
|
|
|
items: followList,
|
2024-01-24 11:43:51 +00:00
|
|
|
discriminator: login.publicKey?.slice(0, 12),
|
|
|
|
extra: rb => {
|
2024-04-22 13:38:14 +00:00
|
|
|
if (login.tags.length > 0) {
|
|
|
|
rb.withFilter().kinds([EventKind.TextNote, EventKind.Repost]).tags(login.tags);
|
2024-01-24 11:43:51 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}) as TimelineSubject,
|
2024-04-22 13:38:14 +00:00
|
|
|
[login.publicKey, followList, login.tags],
|
2024-01-24 11:43:51 +00:00
|
|
|
);
|
2024-02-16 11:17:05 +00:00
|
|
|
const feed = useTimelineFeed(subject, { method: "TIME_RANGE", now: openedAt } as TimelineFeedOptions);
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2024-02-12 10:54:11 +00:00
|
|
|
// TODO allow reposts:
|
2023-11-10 09:57:24 +00:00
|
|
|
const postsOnly = useCallback(
|
|
|
|
(a: NostrEvent) => (props.postsOnly ? !a.tags.some(b => b[0] === "e" || b[0] === "a") : true),
|
|
|
|
[props.postsOnly],
|
|
|
|
);
|
2023-11-10 09:43:02 +00:00
|
|
|
|
2023-09-05 13:59:44 +00:00
|
|
|
const filterPosts = useCallback(
|
2023-11-13 22:55:51 +00:00
|
|
|
(nts: Array<TaggedNostrEvent>) => {
|
2023-09-05 13:59:44 +00:00
|
|
|
const a = nts.filter(a => a.kind !== EventKind.LiveEvent);
|
|
|
|
return a
|
2023-11-10 09:43:02 +00:00
|
|
|
?.filter(postsOnly)
|
2024-01-24 11:43:51 +00:00
|
|
|
.filter(a => props.noteFilter?.(a) ?? true)
|
2024-04-15 21:31:51 +00:00
|
|
|
.filter(a => isFollowing(a.pubkey) || a.tags.filter(a => a[0] === "t").length < 5);
|
2023-09-05 13:59:44 +00:00
|
|
|
},
|
2024-04-15 21:31:51 +00:00
|
|
|
[postsOnly, props.noteFilter, isFollowing],
|
2023-09-05 13:59:44 +00:00
|
|
|
);
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2023-09-05 13:59:44 +00:00
|
|
|
const mainFeed = useMemo(() => {
|
2024-01-24 11:43:51 +00:00
|
|
|
return filterPosts(feed.main ?? []);
|
|
|
|
}, [feed.main, filterPosts]);
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2023-09-05 13:59:44 +00:00
|
|
|
const latestFeed = useMemo(() => {
|
2024-01-24 11:43:51 +00:00
|
|
|
return filterPosts(feed.latest ?? []);
|
|
|
|
}, [feed.latest]);
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2023-09-05 13:59:44 +00:00
|
|
|
const latestAuthors = useMemo(() => {
|
|
|
|
return dedupeByPubkey(latestFeed).map(e => e.pubkey);
|
|
|
|
}, [latestFeed]);
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2023-09-05 13:59:44 +00:00
|
|
|
function onShowLatest(scrollToTop = false) {
|
2024-01-24 11:43:51 +00:00
|
|
|
feed.showLatest();
|
2023-09-05 13:59:44 +00:00
|
|
|
if (scrollToTop) {
|
|
|
|
window.scrollTo(0, 0);
|
2023-09-05 13:57:50 +00:00
|
|
|
}
|
2023-09-05 13:59:44 +00:00
|
|
|
}
|
2023-09-05 13:57:50 +00:00
|
|
|
|
2023-09-05 13:59:44 +00:00
|
|
|
return (
|
|
|
|
<>
|
2023-11-28 20:53:34 +00:00
|
|
|
<DisplayAsSelector
|
|
|
|
show={props.showDisplayAsSelector}
|
|
|
|
activeSelection={displayAs}
|
|
|
|
onSelect={(displayAs: DisplayAs) => setDisplayAs(displayAs)}
|
|
|
|
/>
|
2023-11-09 12:20:53 +00:00
|
|
|
<TimelineRenderer
|
2024-01-24 11:43:51 +00:00
|
|
|
frags={[{ events: mainFeed, refTime: 0 }]}
|
2023-11-09 12:20:53 +00:00
|
|
|
latest={latestAuthors}
|
|
|
|
showLatest={t => onShowLatest(t)}
|
|
|
|
noteOnClick={props.noteOnClick}
|
|
|
|
noteRenderer={props.noteRenderer}
|
2023-11-14 11:43:40 +00:00
|
|
|
noteContext={e => {
|
|
|
|
if (typeof e.context === "string") {
|
|
|
|
return <Link to={`/t/${e.context}`}>{`#${e.context}`}</Link>;
|
|
|
|
}
|
|
|
|
}}
|
2023-11-28 19:41:53 +00:00
|
|
|
displayAs={displayAs}
|
2024-02-02 10:17:14 +00:00
|
|
|
loadMore={() => feed.loadMore()}
|
2023-11-09 12:20:53 +00:00
|
|
|
/>
|
2023-09-05 13:59:44 +00:00
|
|
|
</>
|
|
|
|
);
|
2023-09-05 13:57:50 +00:00
|
|
|
};
|
2023-11-28 19:41:53 +00:00
|
|
|
|
2023-09-05 13:57:50 +00:00
|
|
|
export default TimelineFollows;
|