diff --git a/README.md b/README.md
index 8fa1df20e..d7a06109d 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,21 @@
-# TODO
+# TODO Features
-- Adding loading state to `useUserMetadata` so views can show loading state
-- Add a debounce to user metadata services so it dose not spam the relay when updating subscription
-- user metadata service: remove author from subscription once metadata is returned
-- create a stats page showing state of local db and info about app
-- create user timeline service that caching events and supports loading older events on request
-
-## Ideas
+## MVP
- come up with a clever name
+- cache user timelines for performance
+- add "Load more" button for user timelines
+- build event/thread view
+- build relays tab under user
+- connect to browser extension for signing
+- add simple post form
+- add stats page for debugging and cool stats
+
+## Stage 1
+
- build support for DMs
+- linkify posts
+- detect LN invoices
+- detect LNURL
+- add user tip button
+- create mobile layout
diff --git a/index.html b/index.html
index bee3e4187..347a9fed8 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,11 @@
-
+
+
personal-nostr-client
diff --git a/src/components/icons/global.svg b/src/components/icons/global.svg
new file mode 100644
index 000000000..d4af44a75
--- /dev/null
+++ b/src/components/icons/global.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/icons/home.svg b/src/components/icons/home.svg
new file mode 100644
index 000000000..f5742ceee
--- /dev/null
+++ b/src/components/icons/home.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/icons/settings.svg b/src/components/icons/settings.svg
new file mode 100644
index 000000000..542b57478
--- /dev/null
+++ b/src/components/icons/settings.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/page.tsx b/src/components/page.tsx
index a041346e2..109e6f73c 100644
--- a/src/components/page.tsx
+++ b/src/components/page.tsx
@@ -1,28 +1,85 @@
import React from "react";
-import { Box, Button, Container, HStack, VStack } from "@chakra-ui/react";
+import {
+ Box,
+ Button,
+ Container,
+ Flex,
+ IconButton,
+ VStack,
+} from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { ErrorBoundary } from "./error-boundary";
import { ConnectedRelays } from "./connected-relays";
-export const Page = ({ children }: { children: React.ReactNode }) => {
+import homeIcon from "./icons/home.svg";
+import globalIcon from "./icons/global.svg";
+import settingsIcon from "./icons/settings.svg";
+import { useIsMobile } from "../hooks/use-is-mobile";
+
+const MobileLayout = ({ children }: { children: React.ReactNode }) => {
const navigate = useNavigate();
return (
-
-
-
- navigate("/")}>Home
- navigate("/global")}>Global
- navigate("/settings")}>Settings
-
-
-
- {children}
-
-
- navigate("/")}>Manage Follows
-
-
+
+
+ {children}
+
+
+ }
+ aria-label="Home"
+ onClick={() => navigate("/")}
+ flexGrow="1"
+ size="lg"
+ />
+ }
+ aria-label="Global Feed"
+ onClick={() => navigate("/global")}
+ flexGrow="1"
+ size="lg"
+ />
+ }
+ aria-label="Settings"
+ onClick={() => navigate("/settings")}
+ flexGrow="1"
+ size="lg"
+ />
+
+
+ );
+};
+const DesktopLayout = ({ children }: { children: React.ReactNode }) => {
+ const navigate = useNavigate();
+
+ return (
+
+
+ navigate("/")}>Home
+ navigate("/global")}>Global Feed
+ navigate("/settings")}>Settings
+
+
+
+ {children}
+
+
+ navigate("/")}>Manage Follows
+
);
};
+
+export const Page = ({ children }: { children: React.ReactNode }) => {
+ const isMobile = useIsMobile();
+ const Layout = isMobile ? MobileLayout : DesktopLayout;
+
+ return {children} ;
+};
diff --git a/src/components/post.tsx b/src/components/post.tsx
index 495bec17c..4cfa78f60 100644
--- a/src/components/post.tsx
+++ b/src/components/post.tsx
@@ -5,7 +5,6 @@ import {
Card,
CardBody,
CardHeader,
- Code,
Flex,
Heading,
HStack,
@@ -35,7 +34,7 @@ export const Post = React.memo(({ event }: PostProps) => {
: event.pubkey;
return (
-
+
@@ -50,7 +49,7 @@ export const Post = React.memo(({ event }: PostProps) => {
-
+
@@ -65,7 +64,6 @@ export const Post = React.memo(({ event }: PostProps) => {
>
)}
- {event.id}
diff --git a/src/hooks/use-is-mobile.ts b/src/hooks/use-is-mobile.ts
new file mode 100644
index 000000000..6b30a0712
--- /dev/null
+++ b/src/hooks/use-is-mobile.ts
@@ -0,0 +1,7 @@
+import { useMediaQuery } from "@chakra-ui/react";
+
+export function useIsMobile() {
+ const [isMobile] = useMediaQuery("(max-width: 1000px)");
+
+ return isMobile;
+}
diff --git a/src/services/db/index.ts b/src/services/db/index.ts
index 68aebfe90..7d8ca3de0 100644
--- a/src/services/db/index.ts
+++ b/src/services/db/index.ts
@@ -25,7 +25,15 @@ const MIGRATIONS: MigrationFunction[] = [
// setup data
const settings = db.createObjectStore("settings");
- settings.put(["wss://nostr.rdfriedl.com"], "relays");
+ settings.put(
+ [
+ "wss://nostr.rdfriedl.com",
+ "wss://relay.damus.io",
+ "wss://relay.nostr.info",
+ "wss://nostr.zebedee.cloud",
+ ],
+ "relays"
+ );
},
];
diff --git a/src/styles.css b/src/styles.css
new file mode 100644
index 000000000..073831cb8
--- /dev/null
+++ b/src/styles.css
@@ -0,0 +1,8 @@
+html,
+body,
+#root {
+ margin: 0;
+ height: 100%;
+ width: 100%;
+ overflow: hidden;
+}
diff --git a/src/views/global/index.tsx b/src/views/global/index.tsx
index da95af1a4..a8a0529e8 100644
--- a/src/views/global/index.tsx
+++ b/src/views/global/index.tsx
@@ -41,7 +41,7 @@ export const GlobalView = () => {
if (timeline.length > 20) timeline.length = 20;
return (
-
+
{timeline.map((event) => (
))}
diff --git a/src/views/user/index.tsx b/src/views/user/index.tsx
index 9276706ef..6e0484b8a 100644
--- a/src/views/user/index.tsx
+++ b/src/views/user/index.tsx
@@ -8,15 +8,16 @@ import {
TabPanels,
Tabs,
Text,
- VStack,
} from "@chakra-ui/react";
import { useParams } from "react-router-dom";
import { UserPostsTab } from "./posts";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import { UserAvatar } from "../../components/user-avatar";
import { getUserFullName } from "../../helpers/user-metadata";
+import { useIsMobile } from "../../hooks/use-is-mobile";
export const UserView = () => {
+ const isMobile = useIsMobile();
const { pubkey } = useParams();
if (!pubkey) {
// TODO: better 404
@@ -27,24 +28,34 @@ export const UserView = () => {
const label = metadata ? getUserFullName(metadata) || pubkey : pubkey;
return (
-
- {" "}
-
-
-
- {label}
+
+
+
+
+ {label}
{loadingMetadata ? : {metadata?.about} }
-
+
Posts
Other
Relays
-
-
+
+
@@ -55,6 +66,6 @@ export const UserView = () => {
-
+
);
};
diff --git a/src/views/user/posts.tsx b/src/views/user/posts.tsx
index 4a1d11283..4a8f6e034 100644
--- a/src/views/user/posts.tsx
+++ b/src/views/user/posts.tsx
@@ -43,7 +43,7 @@ export const UserPostsTab = ({ pubkey }: { pubkey: string }) => {
if (timeline.length > 20) timeline.length = 20;
return (
-
+
{timeline.map((event) => (
))}