From 3e5b7ec4a6f1070c587d8fc3eae58116de7a3594 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 15 May 2023 09:51:17 +0100 Subject: [PATCH] feat: discover page --- packages/app/src/Element/ProfilePreview.css | 2 +- .../app/src/Element/SuggestedProfiles.tsx | 36 +++++++++++++++++ packages/app/src/Element/TrendingPosts.tsx | 2 +- packages/app/src/Element/TrendingUsers.tsx | 2 +- packages/app/src/{ => External}/NostrBand.tsx | 14 ++++++- packages/app/src/External/index.ts | 1 + packages/app/src/Pages/Discover.tsx | 40 +++++++++++++++++++ packages/app/src/Pages/Root.tsx | 15 ++++++- packages/app/src/index.tsx | 1 + 9 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 packages/app/src/Element/SuggestedProfiles.tsx rename packages/app/src/{ => External}/NostrBand.tsx (77%) create mode 100644 packages/app/src/External/index.ts create mode 100644 packages/app/src/Pages/Discover.tsx diff --git a/packages/app/src/Element/ProfilePreview.css b/packages/app/src/Element/ProfilePreview.css index c0b412ee2..00211cc0e 100644 --- a/packages/app/src/Element/ProfilePreview.css +++ b/packages/app/src/Element/ProfilePreview.css @@ -1,7 +1,7 @@ .profile-preview { display: flex; align-items: center; - min-height: 40px; + min-height: 59px; } .profile-preview .pfp { diff --git a/packages/app/src/Element/SuggestedProfiles.tsx b/packages/app/src/Element/SuggestedProfiles.tsx new file mode 100644 index 000000000..7ba67e2ec --- /dev/null +++ b/packages/app/src/Element/SuggestedProfiles.tsx @@ -0,0 +1,36 @@ +import { useEffect, useState } from "react"; +import { HexKey, NostrPrefix } from "@snort/nostr"; +import { FormattedMessage } from "react-intl"; + +import FollowListBase from "Element/FollowListBase"; +import PageSpinner from "Element/PageSpinner"; +import NostrBandApi from "External/NostrBand"; +import useLogin from "Hooks/useLogin"; +import { hexToBech32 } from "Util"; + +export default function SuggestedProfiles() { + const login = useLogin(); + const [userList, setUserList] = useState(); + + async function loadSuggestedProfiles() { + const api = new NostrBandApi(); + const users = await api.sugguestedFollows(hexToBech32(NostrPrefix.PublicKey, login.publicKey)); + const keys = users.profiles.map(a => a.pubkey); + setUserList(keys); + } + + useEffect(() => { + loadSuggestedProfiles().catch(console.error); + }, []); + + if (!userList) return ; + + return ( + <> +

+ +

+ + + ); +} diff --git a/packages/app/src/Element/TrendingPosts.tsx b/packages/app/src/Element/TrendingPosts.tsx index 340232e89..b24344980 100644 --- a/packages/app/src/Element/TrendingPosts.tsx +++ b/packages/app/src/Element/TrendingPosts.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from "react-intl"; import PageSpinner from "Element/PageSpinner"; import Note from "Element/Note"; -import NostrBandApi from "NostrBand"; +import NostrBandApi from "External/NostrBand"; export default function TrendingNotes() { const [posts, setPosts] = useState>(); diff --git a/packages/app/src/Element/TrendingUsers.tsx b/packages/app/src/Element/TrendingUsers.tsx index 6d7a0ad60..477cca1f6 100644 --- a/packages/app/src/Element/TrendingUsers.tsx +++ b/packages/app/src/Element/TrendingUsers.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from "react-intl"; import FollowListBase from "Element/FollowListBase"; import PageSpinner from "Element/PageSpinner"; -import NostrBandApi from "NostrBand"; +import NostrBandApi from "External/NostrBand"; export default function TrendingUsers() { const [userList, setUserList] = useState(); diff --git a/packages/app/src/NostrBand.tsx b/packages/app/src/External/NostrBand.tsx similarity index 77% rename from packages/app/src/NostrBand.tsx rename to packages/app/src/External/NostrBand.tsx index 6c74b183c..930edeb07 100644 --- a/packages/app/src/NostrBand.tsx +++ b/packages/app/src/External/NostrBand.tsx @@ -17,6 +17,14 @@ export interface TrendingNoteResponse { notes: Array; } +export interface SuggestedFollow { + pubkey: string; +} + +export interface SuggestedFollowsResponse { + profiles: Array; +} + export class NostrBandError extends Error { body: string; statusCode: number; @@ -29,7 +37,7 @@ export class NostrBandError extends Error { } export default class NostrBandApi { - #url = "https://api.nostr.band"; + readonly #url = "https://api.nostr.band"; async trendingProfiles() { return await this.#json("GET", "/v0/trending/profiles"); @@ -39,6 +47,10 @@ export default class NostrBandApi { return await this.#json("GET", "/v0/trending/notes"); } + async sugguestedFollows(pubkey: string) { + return await this.#json("GET", `/v0/suggested/profiles/${pubkey}`); + } + async #json(method: string, path: string) { const res = await fetch(`${this.#url}${path}`, { method: method ?? "GET", diff --git a/packages/app/src/External/index.ts b/packages/app/src/External/index.ts new file mode 100644 index 000000000..1ab06e0eb --- /dev/null +++ b/packages/app/src/External/index.ts @@ -0,0 +1 @@ +export * from "./NostrBand"; diff --git a/packages/app/src/Pages/Discover.tsx b/packages/app/src/Pages/Discover.tsx new file mode 100644 index 000000000..989372365 --- /dev/null +++ b/packages/app/src/Pages/Discover.tsx @@ -0,0 +1,40 @@ +import SuggestedProfiles from "Element/SuggestedProfiles"; +import { Tab, TabElement } from "Element/Tabs"; +import TrendingNotes from "Element/TrendingPosts"; +import TrendingUsers from "Element/TrendingUsers"; +import { useState } from "react"; +import { useIntl } from "react-intl"; + +export default function Discover() { + const { formatMessage } = useIntl(); + // tabs + const Tabs = { + Follows: { text: formatMessage({ defaultMessage: "Suggested Follows" }), value: 0 }, + Posts: { text: formatMessage({ defaultMessage: "Trending Notes" }), value: 1 }, + Profiles: { text: formatMessage({ defaultMessage: "Trending People" }), value: 2 }, + }; + const [tab, setTab] = useState(Tabs.Follows); + + function renderTab() { + switch (tab.value) { + case 0: + return ; + case 1: + return ; + case 2: + return ; + } + return null; + } + + return ( + <> +
+ {[Tabs.Follows, Tabs.Posts, Tabs.Profiles].map(a => ( + + ))} +
+ {renderTab()} + + ); +} diff --git a/packages/app/src/Pages/Root.tsx b/packages/app/src/Pages/Root.tsx index f9c1b7f47..0ac7cc2e3 100644 --- a/packages/app/src/Pages/Root.tsx +++ b/packages/app/src/Pages/Root.tsx @@ -9,6 +9,7 @@ import { System } from "System"; import { TimelineSubject } from "Feed/TimelineFeed"; import { debounce, getRelayName, sha256, unixNow, unwrap } from "Util"; import useLogin from "Hooks/useLogin"; +import Discover from "Pages/Discover"; import messages from "./messages"; @@ -39,12 +40,17 @@ export default function RootPage() { value: 2, data: "/global", }, + Discover: { + text: formatMessage({ defaultMessage: "Discover" }), + value: 3, + data: "/discover", + }, }; 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 tabs = [RootTab.Posts, RootTab.PostsAndReplies, RootTab.Global, RootTab.Discover, ...tagTabs]; const tab = useMemo(() => { const pTab = location.pathname.split("/").slice(-1)[0]; @@ -63,6 +69,9 @@ export default function RootPage() { case "global": { return RootTab.Global; } + case "discover": { + return RootTab.Discover; + } default: { return RootTab.Posts; } @@ -237,6 +246,10 @@ export const RootRoutes = [ path: "conversations", element: , }, + { + path: "discover", + element: , + }, { path: "tag/:tag", element: , diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 0f4f420c8..788af7057 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -30,6 +30,7 @@ import { WalletRoutes } from "Pages/WalletPage"; import NostrLinkHandler from "Pages/NostrLinkHandler"; import Thread from "Element/Thread"; import { SubscribeRoutes } from "Pages/subscribe"; +import Discover from "Pages/Discover"; /** * HTTP query provider