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,