import "./Root.css"; import { useEffect, useMemo, useState } from "react"; import { Link, Outlet, RouteObject, useLocation, useNavigate, useParams } from "react-router-dom"; import { useIntl, FormattedMessage } from "react-intl"; import Tabs, { Tab } from "Element/Tabs"; import Timeline from "Element/Timeline"; import { System } from "System"; import { TimelineSubject } from "Feed/TimelineFeed"; import { debounce, getRelayName, sha256, unixNow, unwrap } from "Util"; import useLogin from "Hooks/useLogin"; import messages from "./messages"; interface RelayOption { url: string; paid: boolean; } export default function RootPage() { const { formatMessage } = useIntl(); const navigate = useNavigate(); const location = useLocation(); const { publicKey: pubKey, tags, preferences } = useLogin(); const RootTab: Record = { Posts: { text: formatMessage(messages.Posts), value: 0, data: "/posts", }, PostsAndReplies: { text: formatMessage(messages.Conversations), value: 1, data: "/conversations", }, Global: { text: formatMessage(messages.Global), value: 2, data: "/global", }, }; const tagTabs = tags.item.map((t, idx) => { return { text: `#${t}`, value: idx + 3, data: `/tag/${t}` }; }); const tabs = [RootTab.Posts, RootTab.PostsAndReplies, RootTab.Global, ...tagTabs]; const tab = useMemo(() => { const pTab = location.pathname.split("/").slice(-1)[0]; if (location.pathname.startsWith("/tag")) { const selectedTag = tagTabs.find(t => t.text.slice(1) === pTab); if (selectedTag) { return selectedTag; } } switch (pTab) { case "conversations": { return RootTab.PostsAndReplies; } case "global": { return RootTab.Global; } default: { return RootTab.Posts; } } }, [location]); useEffect(() => { if (location.pathname === "/") { const t = pubKey ? preferences.defaultRootTab ?? tab.data : "/global"; navigate(t, { replace: true, }); } }, [location]); return ( <>
{pubKey && navigate(unwrap(t.data))} />}
); } const FollowsHint = () => { const { publicKey: pubKey, follows } = useLogin(); if (follows.item?.length === 0 && pubKey) { return ( ), }} /> ); } return null; }; const GlobalTab = () => { const { relays } = useLogin(); const [relay, setRelay] = useState(); const [allRelays, setAllRelays] = useState(); const [now] = useState(unixNow()); const subject: TimelineSubject = { type: "global", items: [], discriminator: `all-${sha256(relay?.url ?? "").slice(0, 12)}`, }; function globalRelaySelector() { if (!allRelays || allRelays.length === 0) return null; const paidRelays = allRelays.filter(a => a.paid); const publicRelays = allRelays.filter(a => !a.paid); return (
 
); } useEffect(() => { return debounce(500, () => { const ret: RelayOption[] = []; System.Sockets.forEach((v, k) => { ret.push({ url: k, paid: v.Info?.limitation?.payment_required ?? false, }); }); ret.sort(a => (a.paid ? -1 : 1)); if (ret.length > 0 && !relay) { setRelay(ret[0]); } setAllRelays(ret); }); }, [relays, relay]); return ( <> {globalRelaySelector()} {relay && ( )} ); }; const PostsTab = () => { const { follows, publicKey } = useLogin(); const subject: TimelineSubject = { type: "pubkey", items: follows.item, discriminator: `follows:${publicKey?.slice(0, 12)}`, }; return ( <> ); }; const ConversationsTab = () => { const { follows, publicKey } = useLogin(); const subject: TimelineSubject = { type: "pubkey", items: follows.item, discriminator: `follows:${publicKey?.slice(0, 12)}`, }; return ; }; const TagsTab = () => { const { tag } = useParams(); const subject: TimelineSubject = { type: "hashtag", items: [tag ?? ""], discriminator: `tags-${tag}` }; return ; }; export const RootRoutes = [ { path: "/", element: , children: [ { path: "global", element: , }, { path: "posts", element: , }, { path: "conversations", element: , }, { path: "tag/:tag", element: , }, ], }, ] as RouteObject[];