diff --git a/src/App.tsx b/src/App.tsx index 3a0f94d..c369eeb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import { SearchProvider } from './contexts/SearchContext'; import { MessagesProvider } from './contexts/MessagesContext'; import { MediaProvider } from './contexts/MediaContext'; import { AppProvider } from './contexts/AppContext'; +import { ReadsProvider } from './contexts/ReadsContext'; export const APP_ID = `${Math.floor(Math.random()*10000000000)}`; @@ -39,13 +40,15 @@ const App: Component = () => { - - - - - - - + + + + + + + + + diff --git a/src/Router.tsx b/src/Router.tsx index be33e78..0c035b7 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -16,6 +16,7 @@ import { useNotificationsContext } from './contexts/NotificationsContext'; import { useSearchContext } from './contexts/SearchContext'; const Home = lazy(() => import('./pages/Home')); +const Reads = lazy(() => import('./pages/Reads')); const Layout = lazy(() => import('./components/Layout/Layout')); const Explore = lazy(() => import('./pages/Explore')); const Thread = lazy(() => import('./pages/Thread')); @@ -109,6 +110,7 @@ const Router: Component = () => { + diff --git a/src/assets/icons/nav/bookmarks.svg b/src/assets/icons/nav/bookmarks.svg new file mode 100644 index 0000000..c25ef0f --- /dev/null +++ b/src/assets/icons/nav/bookmarks.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/bookmarks_selected.svg b/src/assets/icons/nav/bookmarks_selected.svg new file mode 100644 index 0000000..380dbd2 --- /dev/null +++ b/src/assets/icons/nav/bookmarks_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/downloads.svg b/src/assets/icons/nav/downloads.svg new file mode 100644 index 0000000..634d0fa --- /dev/null +++ b/src/assets/icons/nav/downloads.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/nav/downloads_selected.svg b/src/assets/icons/nav/downloads_selected.svg new file mode 100644 index 0000000..338a19f --- /dev/null +++ b/src/assets/icons/nav/downloads_selected.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/nav/home.svg b/src/assets/icons/nav/home.svg new file mode 100644 index 0000000..ab377dc --- /dev/null +++ b/src/assets/icons/nav/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/home_selected.svg b/src/assets/icons/nav/home_selected.svg new file mode 100644 index 0000000..faebf0f --- /dev/null +++ b/src/assets/icons/nav/home_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/long.svg b/src/assets/icons/nav/long.svg new file mode 100644 index 0000000..0129afa --- /dev/null +++ b/src/assets/icons/nav/long.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/nav/long_selected.svg b/src/assets/icons/nav/long_selected.svg new file mode 100644 index 0000000..169f5b2 --- /dev/null +++ b/src/assets/icons/nav/long_selected.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/nav/messages.svg b/src/assets/icons/nav/messages.svg new file mode 100644 index 0000000..9d9e918 --- /dev/null +++ b/src/assets/icons/nav/messages.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/messages_selected.svg b/src/assets/icons/nav/messages_selected.svg new file mode 100644 index 0000000..027ca98 --- /dev/null +++ b/src/assets/icons/nav/messages_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/notifications.svg b/src/assets/icons/nav/notifications.svg new file mode 100644 index 0000000..f1265dc --- /dev/null +++ b/src/assets/icons/nav/notifications.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/notifications_selected.svg b/src/assets/icons/nav/notifications_selected.svg new file mode 100644 index 0000000..f0a8568 --- /dev/null +++ b/src/assets/icons/nav/notifications_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/plus.svg b/src/assets/icons/nav/plus.svg new file mode 100644 index 0000000..e7474ff --- /dev/null +++ b/src/assets/icons/nav/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/search.svg b/src/assets/icons/nav/search.svg new file mode 100644 index 0000000..7a81f2e --- /dev/null +++ b/src/assets/icons/nav/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/search_selected.svg b/src/assets/icons/nav/search_selected.svg new file mode 100644 index 0000000..0b022cc --- /dev/null +++ b/src/assets/icons/nav/search_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/nav/settings.svg b/src/assets/icons/nav/settings.svg new file mode 100644 index 0000000..970a88b --- /dev/null +++ b/src/assets/icons/nav/settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/nav/settings_selected.svg b/src/assets/icons/nav/settings_selected.svg new file mode 100644 index 0000000..d751afd --- /dev/null +++ b/src/assets/icons/nav/settings_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/NavLink/NavLink.module.scss b/src/components/NavLink/NavLink.module.scss index 79274a4..fd8fcdf 100644 --- a/src/components/NavLink/NavLink.module.scss +++ b/src/components/NavLink/NavLink.module.scss @@ -39,44 +39,96 @@ .homeIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/home.svg) no-repeat center; - mask: url(../../assets/icons/home.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/home.svg) no-repeat center; + mask: url(../../assets/icons/nav/home.svg) no-repeat center; +} +.readsIcon { + @include iconNav; + -webkit-mask: url(../../assets/icons/nav/long.svg) no-repeat center; + mask: url(../../assets/icons/nav/long.svg) no-repeat center; } - .exploreIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/search.svg) no-repeat center; - mask: url(../../assets/icons/search.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/search.svg) no-repeat center; + mask: url(../../assets/icons/nav/search.svg) no-repeat center; } .messagesIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/messages.svg) no-repeat center; - mask: url(../../assets/icons/messages.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/messages.svg) no-repeat center; + mask: url(../../assets/icons/nav/messages.svg) no-repeat center; } .notificationsIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/notifications.svg) no-repeat center; - mask: url(../../assets/icons/notifications.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/notifications.svg) no-repeat center; + mask: url(../../assets/icons/nav/notifications.svg) no-repeat center; } .downloadIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/download.svg) no-repeat center; - mask: url(../../assets/icons/download.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/downloads.svg) no-repeat center; + mask: url(../../assets/icons/nav/downloads.svg) no-repeat center; } .settingsIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/settings.svg) no-repeat center; - mask: url(../../assets/icons/settings.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/settings.svg) no-repeat center; + mask: url(../../assets/icons/nav/settings.svg) no-repeat center; } .helpIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/help.svg) no-repeat center; - mask: url(../../assets/icons/help.svg) no-repeat center; + -webkit-mask: url(../../assets/icons/nav/help.svg) no-repeat center; + mask: url(../../assets/icons/nav/help.svg) no-repeat center; } .bookmarkIcon { @include iconNav; - -webkit-mask: url(../../assets/icons/bookmark_empty.svg) no-repeat 0 / auto 100%; - mask: url(../../assets/icons/bookmark_empty.svg) no-repeat center 0 / auto 100%; + -webkit-mask: url(../../assets/icons/nav/bookmarks.svg) no-repeat 0 / auto 100%; + mask: url(../../assets/icons/nav/bookmarks.svg) no-repeat center 0 / auto 100%; +} + +&:hover, a.active { + .homeIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/home_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/home_selected.svg) no-repeat center; + } + .readsIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/long_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/long_selected.svg) no-repeat center; + } + .exploreIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/search_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/search_selected.svg) no-repeat center; + } + .messagesIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/messages_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/messages_selected.svg) no-repeat center; + } + .notificationsIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/notifications_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/notifications_selected.svg) no-repeat center; + } + .downloadIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/downloads_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/downloads_selected.svg) no-repeat center; + } + .settingsIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/settings_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/settings_selected.svg) no-repeat center; + } + .helpIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/help_selected.svg) no-repeat center; + mask: url(../../assets/icons/nav/help_selected.svg) no-repeat center; + } + .bookmarkIcon { + background-color: var(--text-primary); + -webkit-mask: url(../../assets/icons/nav/bookmarks_selected.svg) no-repeat 0 / auto 100%; + mask: url(../../assets/icons/nav/bookmarks_selected.svg) no-repeat center 0 / auto 100%; + } } .active { diff --git a/src/components/NavMenu/NavMenu.tsx b/src/components/NavMenu/NavMenu.tsx index 6c58529..aff0b1a 100644 --- a/src/components/NavMenu/NavMenu.tsx +++ b/src/components/NavMenu/NavMenu.tsx @@ -27,6 +27,11 @@ const NavMenu: Component< { id?: string } > = (props) => { label: intl.formatMessage(t.home), icon: 'homeIcon', }, + { + to: '/reads', + label: intl.formatMessage(t.reads), + icon: 'readsIcon', + }, { to: '/explore', label: intl.formatMessage(t.explore), diff --git a/src/contexts/ReadsContext.tsx b/src/contexts/ReadsContext.tsx new file mode 100644 index 0000000..fa3b906 --- /dev/null +++ b/src/contexts/ReadsContext.tsx @@ -0,0 +1,274 @@ +import { nip19 } from "nostr-tools"; +import { createContext, createEffect, onCleanup, useContext } from "solid-js"; +import { createStore, reconcile, unwrap } from "solid-js/store"; +import { APP_ID } from "../App"; +import { Kind } from "../constants"; +import { getArticlesFeed, getEvents, getExploreFeed, getFeed, getFutureExploreFeed, getFutureFeed } from "../lib/feed"; +import { fetchStoredFeed, saveStoredFeed } from "../lib/localStore"; +import { setLinkPreviews } from "../lib/notes"; +import { getScoredUsers, searchContent } from "../lib/search"; +import { isConnected, refreshSocketListeners, removeSocketListeners, socket, subscribeTo, subsTo } from "../sockets"; +import { sortingPlan, convertToNotes, parseEmptyReposts, paginationPlan, isInTags, isRepostInCollection } from "../stores/note"; +import { + ContextChildren, + FeedPage, + NostrEOSE, + NostrEvent, + NostrEventContent, + NostrMentionContent, + NostrNoteActionsContent, + NostrNoteContent, + NostrStatsContent, + NostrUserContent, + NoteActions, + PrimalArticle, + PrimalFeed, + PrimalNote, + SelectionOption, + TopZap, +} from "../types/primal"; +import { parseBolt11 } from "../utils"; +import { useAccountContext } from "./AccountContext"; +import { useSettingsContext } from "./SettingsContext"; + +type Event = any; + +type EventPage = Record; + +type ReadsContextData = { + events: Record, + pages: EventPage[], + currentPageNumber: number, +} + +type ReadsContextStore = ReadsContextData & { + actions: { + fetchPage: (page: number, kind: number) => void; + } +} + +const initialData: ReadsContextData = { + events: {}, + pages: [], + currentPageNumber: 0, +}; + +export const ReadsContext = createContext(); + +export const ReadsProvider = (props: { children: ContextChildren }) => { + + const settings = useSettingsContext(); + const account = useAccountContext(); + + const handleEvent = (event: NostrEventContent, page: number) => { + const { kind, content } = event; + + if (!store.pages[page]) { + updateStore('pages', page, {}) + } + + if (!store.pages[page][kind]) { + updateStore('pages', page, { [kind]: { events: [], since: 0, until: 0 }}) + } + + updateStore('pages', page, kind, 'events', (es) => [...es, content]); + }; + + const handleEose = (page: number) => { + console.log('STORE: ', store.pages); + }; + +// ACTIONS -------------------------------------- + + const fetchPage = (page: number, kind: number) => { + const subId = `e_${kind}_${page}_${APP_ID}`; + + const unsub = subsTo(subId, { + onEvent: (_, content) => { + handleEvent(content, page); + }, + onEose: (_) => { + handleEose(page); + unsub(); + }, + onNotice: (_, reason) => {}, + }); + + const until = 0; + const limit = 10; + const offset = 0; + + if (kind === Kind.LongForm) { + getArticlesFeed( + account?.publicKey, + account?.publicKey, + subId, + until, + limit, + offset, + ) + } + }; + +// SOCKET HANDLERS ------------------------------ + + // const onMessage = (event: MessageEvent) => { + // const message: NostrEvent | NostrEOSE = JSON.parse(event.data); + + // const [type, subId, content] = message; + + // if (subId === `home_sidebar_${APP_ID}`) { + // if (type === 'EOSE') { + // saveSidebarPage(store.sidebar.page); + // return; + // } + + // if (!content) { + // return; + // } + + + // if (type === 'EVENT') { + // updateSidebarPage(content); + // return; + // } + // } + + // if (subId === `home_feed_${APP_ID}`) { + // if (type === 'EOSE') { + // const reposts = parseEmptyReposts(store.page); + // const ids = Object.keys(reposts); + + // if (ids.length === 0) { + // savePage(store.page); + // return; + // } + + // updateStore('reposts', () => reposts); + + // getEvents(account?.publicKey, ids, `home_reposts_${APP_ID}`); + + // return; + // } + + // if (type === 'EVENT') { + // updatePage(content); + // return; + // } + // } + + // if (subId === `home_reposts_${APP_ID}`) { + // if (type === 'EOSE') { + // savePage(store.page); + // return; + // } + + // if (type === 'EVENT') { + // const repostId = (content as NostrNoteContent).id; + // const reposts = store.reposts || {}; + // const parent = store.page.messages.find(m => m.id === reposts[repostId]); + + // if (parent) { + // updateStore('page', 'messages', (msg) => msg.id === parent.id, 'content', () => JSON.stringify(content)); + // } + + // return; + // } + // } + + // if (subId === `home_future_${APP_ID}`) { + // if (type === 'EOSE') { + // const reposts = parseEmptyReposts(store.future.page); + // const ids = Object.keys(reposts); + + // if (ids.length === 0) { + // savePage(store.future.page, 'future'); + // return; + // } + + // updateStore('future', 'reposts', () => reposts); + + // getEvents(account?.publicKey, ids, `home_future_reposts_${APP_ID}`); + + // return; + // } + + // if (type === 'EVENT') { + // updatePage(content, 'future'); + // return; + // } + // } + + // if (subId === `home_future_reposts_${APP_ID}`) { + // if (type === 'EOSE') { + // savePage(store.future.page, 'future'); + // return; + // } + + // if (type === 'EVENT') { + // const repostId = (content as NostrNoteContent).id; + // const reposts = store.future.reposts || {}; + // const parent = store.future.page.messages.find(m => m.id === reposts[repostId]); + + // if (parent) { + // updateStore('future', 'page', 'messages', (msg) => msg.id === parent.id, 'content', () => JSON.stringify(content)); + // } + + // return; + // } + // } + + + // }; + + // const onSocketClose = (closeEvent: CloseEvent) => { + // const webSocket = closeEvent.target as WebSocket; + + // removeSocketListeners( + // webSocket, + // { message: onMessage, close: onSocketClose }, + // ); + // }; + +// EFFECTS -------------------------------------- + + // createEffect(() => { + // if (isConnected()) { + // refreshSocketListeners( + // socket(), + // { message: onMessage, close: onSocketClose }, + // ); + // } + // }); + + // onCleanup(() => { + // removeSocketListeners( + // socket(), + // { message: onMessage, close: onSocketClose }, + // ); + // }); + + +// STORES --------------------------------------- + + const [store, updateStore] = createStore({ + ...initialData, + actions: { + fetchPage, + }, + }); + +// RENDER ------------------------------------- + + return ( + + {props.children} + + ); +} + +export const useReadsContext = () => useContext(ReadsContext); diff --git a/src/index.scss b/src/index.scss index 0af4624..d72e301 100644 --- a/src/index.scss +++ b/src/index.scss @@ -126,6 +126,27 @@ a { border: 2px solid red; } +body::after{ + position:absolute; width:0; height:0; overflow:hidden; z-index:-1; // hide images + content: + url(./assets/icons/nav/bookmarks.svg) + url(./assets/icons/nav/bookmarks_selected.svg) + url(./assets/icons/nav/home.svg) + url(./assets/icons/nav/home_selected.svg) + url(./assets/icons/nav/search.svg) + url(./assets/icons/nav/search_selected.svg) + url(./assets/icons/nav/messages.svg) + url(./assets/icons/nav/messages_selected.svg) + url(./assets/icons/nav/notifications.svg) + url(./assets/icons/nav/notifications_selected.svg) + url(./assets/icons/nav/downloads.svg) + url(./assets/icons/nav/downloads_selected.svg) + url(./assets/icons/nav/settings.svg) + url(./assets/icons/nav/settings_selected.svg) + url(./assets/icons/nav/long.svg) + url(./assets/icons/nav/long_selected.svg); +} + .reply_icon { -webkit-mask: url(./assets/icons/feed_reply.svg) no-repeat 0 / 100%; mask: url(./assets/icons/feed_reply_fill.svg) no-repeat 0 / 100%; diff --git a/src/lib/feed.ts b/src/lib/feed.ts index 3e7552f..5dbe9d3 100644 --- a/src/lib/feed.ts +++ b/src/lib/feed.ts @@ -46,6 +46,26 @@ export const getFeed = (user_pubkey: string | undefined, pubkey: string | undef ])); } +export const getArticlesFeed = (user_pubkey: string | undefined, pubkey: string | undefined, subid: string, until = 0, limit = 20, offset=0) => { + if (!pubkey) { + return; + } + + const start = until === 0 ? 'since' : 'until'; + + let payload = { limit, [start]: until, pubkey, offset }; + + if (user_pubkey) { + payload.user_pubkey = user_pubkey; + } + + sendMessage(JSON.stringify([ + "REQ", + subid, + {cache: ["long_form_content_feed", payload]}, + ])); +} + export const getEvents = (user_pubkey: string | undefined, eventIds: string[], subid: string, extendResponse?: boolean) => { let payload: {event_ids: string[], user_pubkey?: string, extended_response?: boolean } = diff --git a/src/pages/Reads.tsx b/src/pages/Reads.tsx new file mode 100644 index 0000000..0ccc6ee --- /dev/null +++ b/src/pages/Reads.tsx @@ -0,0 +1,65 @@ +import { + Component, + createEffect, + createSignal, + For, + Match, + onCleanup, + onMount, + Show, + Switch +} from 'solid-js'; +import Note from '../components/Note/Note'; +import styles from './Home.module.scss'; +import HomeHeader from '../components/HomeHeader/HomeHeader'; +import Loader from '../components/Loader/Loader'; +import Paginator from '../components/Paginator/Paginator'; +import HomeSidebar from '../components/HomeSidebar/HomeSidebar'; +import Branding from '../components/Branding/Branding'; +import HomeHeaderPhone from '../components/HomeHeaderPhone/HomeHeaderPhone'; +import Wormhole from '../components/Wormhole/Wormhole'; +import { scrollWindowTo } from '../lib/scroll'; +import StickySidebar from '../components/StickySidebar/StickySidebar'; +import { useHomeContext } from '../contexts/HomeContext'; +import { useIntl } from '@cookbook/solid-intl'; +import { createStore } from 'solid-js/store'; +import { PrimalUser } from '../types/primal'; +import Avatar from '../components/Avatar/Avatar'; +import { userName } from '../stores/profile'; +import { useAccountContext } from '../contexts/AccountContext'; +import { reads as tReads, branding } from '../translations'; +import Search from '../components/Search/Search'; +import { setIsHome } from '../components/Layout/Layout'; +import PageTitle from '../components/PageTitle/PageTitle'; +import { useAppContext } from '../contexts/AppContext'; +import PageCaption from '../components/PageCaption/PageCaption'; +import { useReadsContext } from '../contexts/ReadsContext'; +import { Kind } from '../constants'; + + +const Reads: Component = () => { + + const intl = useIntl(); + const reads = useReadsContext(); + + onMount(() => { + reads?.actions.fetchPage(0, Kind.LongForm); + }); + + return ( +
+ + + + + + + + +
+ ) +} + +export default Reads; diff --git a/src/sockets.tsx b/src/sockets.tsx index 40c174b..1567c55 100644 --- a/src/sockets.tsx +++ b/src/sockets.tsx @@ -1,6 +1,6 @@ import { createSignal } from "solid-js"; import { logError, logInfo } from "./lib/logger"; -import { NostrEvent, NostrEOSE, NostrEventType, NostrEventContent, PrimalWindow } from "./types/primal"; +import { NostrEvent, NostrEOSE, NostrEventType, NostrEventContent, PrimalWindow, NostrNotice } from "./types/primal"; export const [socket, setSocket] = createSignal(); @@ -142,3 +142,39 @@ export const subTo = (socket: WebSocket, subId: string, cb: (type: NostrEventTyp socket.removeEventListener('message', listener); }; }; + + +export const subsTo = ( + subId: string, + handlers?: { + onEvent?: (subId: string, content: NostrEventContent) => void, + onNotice?: (subId: string, reason: string) => void, + onEose?: (subId: string) => void, + }, +) => { + const listener = (event: MessageEvent) => { + const message: NostrEvent | NostrEOSE | NostrNotice = JSON.parse(event.data); + const [type, subscriptionId] = message; + + if (handlers && subId === subscriptionId) { + if (type === 'EVENT') { + handlers.onEvent && handlers.onEvent(subscriptionId, message[2]); + } + + if (type === 'EOSE') { + handlers.onEose && handlers.onEose(subscriptionId); + } + + if (type === 'NOTICE') { + handlers.onNotice && handlers.onNotice(subscriptionId, message[2]) + } + } + + }; + + socket()?.addEventListener('message', listener); + + return () => { + socket()?.removeEventListener('message', listener); + }; +}; diff --git a/src/translations.ts b/src/translations.ts index c012cba..1bd4719 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -709,6 +709,11 @@ export const navBar = { defaultMessage: 'Home', description: 'Label for the nav bar item link to Home page', }, + reads: { + id: 'navbar.reads', + defaultMessage: 'Reads', + description: 'Label for the nav bar item link to Reads page', + }, explore: { id: 'navbar.explore', defaultMessage: 'Explore', @@ -2127,6 +2132,14 @@ export const followWarning = { }, }; +export const reads = { + pageTitle: { + id: 'reads.pageTitle', + defaultMessage: 'Reads', + description: 'Reads page title', + }, +}; + export const bookmarks = { pageTitle: { id: 'bookmarks.pageTitle', diff --git a/src/types/primal.d.ts b/src/types/primal.d.ts index 88cb474..a8f3105 100644 --- a/src/types/primal.d.ts +++ b/src/types/primal.d.ts @@ -279,6 +279,12 @@ export type NostrEOSE = [ subkey: string, ]; +export type NostrNotice = [ + type: "NOTICE", + subkey: string, + reason: string, +]; + export type NoteActions = { event_id: string, liked: boolean, @@ -486,6 +492,17 @@ export type PrimalNote = { topZaps: TopZap[], }; +export type PrimalArticle = { + title: string, + summary: string, + image: string, + tags: string[], + published: number, + content: string, + author: PrimalUser, + topZaps: TopZap[], +}; + export type PrimalFeed = { name: string, npub?: string,