mirror of
https://gitlab.com/soapbox-pub/mkstack.git
synced 2025-08-27 13:09:22 +00:00
193 lines
6.7 KiB
Markdown
193 lines
6.7 KiB
Markdown
# Project Overview
|
|
|
|
This project is a Nostr client application built with React 18.x, TailwindCSS 3.x, Vite, shadcn/ui, and Nostrify.
|
|
|
|
## Technology Stack
|
|
|
|
- **React 18.x**: Stable version of React with hooks, concurrent rendering, and improved performance
|
|
- **TailwindCSS 3.x**: Utility-first CSS framework for styling
|
|
- **Vite**: Fast build tool and development server
|
|
- **shadcn/ui**: Unstyled, accessible UI components built with Radix UI and Tailwind
|
|
- **Nostrify**: Nostr protocol framework for Deno and web
|
|
- **React Router**: For client-side routing
|
|
- **TanStack Query**: For data fetching, caching, and state management
|
|
- **TypeScript**: For type-safe JavaScript development
|
|
|
|
## Project Structure
|
|
|
|
- `/src/components/`: UI components including NostrProvider for Nostr integration
|
|
- `/src/hooks/`: Custom hooks including `useNostr` and `useNostrQuery`
|
|
- `/src/pages/`: Page components used by React Router
|
|
- `/src/lib/`: Utility functions and shared logic
|
|
- `/public/`: Static assets
|
|
|
|
## UI Components
|
|
|
|
The project uses shadcn/ui components located in `@/components/ui`. These are unstyled, accessible components built with Radix UI and styled with Tailwind CSS. Available components include:
|
|
|
|
- **Accordion**: Vertically collapsing content panels
|
|
- **Alert**: Displays important messages to users
|
|
- **AlertDialog**: Modal dialog for critical actions requiring confirmation
|
|
- **AspectRatio**: Maintains consistent width-to-height ratio
|
|
- **Avatar**: User profile pictures with fallback support
|
|
- **Badge**: Small status descriptors for UI elements
|
|
- **Breadcrumb**: Navigation aid showing current location in hierarchy
|
|
- **Button**: Customizable button with multiple variants and sizes
|
|
- **Calendar**: Date picker component
|
|
- **Card**: Container with header, content, and footer sections
|
|
- **Carousel**: Slideshow for cycling through elements
|
|
- **Chart**: Data visualization component
|
|
- **Checkbox**: Selectable input element
|
|
- **Collapsible**: Toggle for showing/hiding content
|
|
- **Command**: Command palette for keyboard-first interfaces
|
|
- **ContextMenu**: Right-click menu component
|
|
- **Dialog**: Modal window overlay
|
|
- **Drawer**: Side-sliding panel
|
|
- **DropdownMenu**: Menu that appears from a trigger element
|
|
- **Form**: Form validation and submission handling
|
|
- **HoverCard**: Card that appears when hovering over an element
|
|
- **InputOTP**: One-time password input field
|
|
- **Input**: Text input field
|
|
- **Label**: Accessible form labels
|
|
- **Menubar**: Horizontal menu with dropdowns
|
|
- **NavigationMenu**: Accessible navigation component
|
|
- **Pagination**: Controls for navigating between pages
|
|
- **Popover**: Floating content triggered by a button
|
|
- **Progress**: Progress indicator
|
|
- **RadioGroup**: Group of radio inputs
|
|
- **Resizable**: Resizable panels and interfaces
|
|
- **ScrollArea**: Scrollable container with custom scrollbars
|
|
- **Select**: Dropdown selection component
|
|
- **Separator**: Visual divider between content
|
|
- **Sheet**: Side-anchored dialog component
|
|
- **Sidebar**: Navigation sidebar component
|
|
- **Skeleton**: Loading placeholder
|
|
- **Slider**: Input for selecting a value from a range
|
|
- **Sonner**: Toast notification manager
|
|
- **Switch**: Toggle switch control
|
|
- **Table**: Data table with headers and rows
|
|
- **Tabs**: Tabbed interface component
|
|
- **Textarea**: Multi-line text input
|
|
- **Toast**: Toast notification component
|
|
- **ToggleGroup**: Group of toggle buttons
|
|
- **Toggle**: Two-state button
|
|
- **Tooltip**: Informational text that appears on hover
|
|
|
|
These components follow a consistent pattern using React's `forwardRef` and use the `cn()` utility for class name merging. Many are built on Radix UI primitives for accessibility and customized with Tailwind CSS.
|
|
|
|
## Nostr Protocol Integration
|
|
|
|
This project comes with custom hooks for querying and publishing events on the Nostr network.
|
|
|
|
### The `useNostr` Hook
|
|
|
|
The `useNostr` hook returns an object containing a `nostr` property, with `.query()` and `.event()` methods for querying and publishing Nostr events respectively.
|
|
|
|
```typescript
|
|
import { useNostr } from '@nostrify/react';
|
|
|
|
function useCustomHook() {
|
|
const { nostr } = useNostr();
|
|
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Query Nostr Data with `useNostr` and Tanstack Query
|
|
|
|
When querying Nostr, the best practice is to create custom hooks that combine `useNostr` and `useQuery` to get the required data.
|
|
|
|
```typescript
|
|
import { useNostr } from '@nostrify/react';
|
|
import { useQuery } from '@tanstack/query';
|
|
|
|
function usePosts() {
|
|
const { nostr } = useNostr();
|
|
|
|
return useQuery({
|
|
queryKey: ['posts'],
|
|
queryFn: async () => {
|
|
const events = await nostr.query([{ kinds: [1], limit: 20 }]);
|
|
return events; // these events could be transformed into another format
|
|
},
|
|
});
|
|
}
|
|
```
|
|
|
|
The data may be transformed into a more appropriate format if needed, and multiple calls to `nostr.query()` may be made in a single queryFn.
|
|
|
|
### The `useNostrPublish` Hook
|
|
|
|
To publish events, use the `useNostrPublish` hook in this project.
|
|
|
|
```tsx
|
|
import { useState } from 'react';
|
|
|
|
import { useCurrentUser } from "@/hooks/useCurrentUser";
|
|
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
|
|
|
export function MyComponent() {
|
|
const [ data, setData] = useState<Record<string, string>>({});
|
|
|
|
const { user } = useCurrentUser();
|
|
const { mutate: createEvent } = useNostrPublish();
|
|
|
|
const handleSubmit = () => {
|
|
createEvent({ kind: 1, content: data.content });
|
|
};
|
|
|
|
if (!user) {
|
|
return <span>You must be logged in to use this form.</span>;
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} disabled={!user}>
|
|
{/* ...some input fields */}
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
The `useCurrentUser` hook should be used to ensure that the user is logged in before they are able to publish Nostr events.
|
|
|
|
### Nostr Login
|
|
|
|
Nostr supports several types of logins:
|
|
|
|
1. Login with nsec
|
|
2. Login with browser extension
|
|
3. Login with bunker URI
|
|
|
|
Functions to log in with each of these methods are exposed by the `useLoginActions` hook:
|
|
|
|
```tsx
|
|
function MyComponent() {
|
|
const login = useLoginActions();
|
|
|
|
login.nsec(nsec); // login by the user pasting their secret key
|
|
login.bunker(uri); // login by the user pasting a bunker URI
|
|
login.extension(); // login with a NIP-07 browser extension
|
|
|
|
return (
|
|
<div>{/* ... */}</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Development Practices
|
|
|
|
- Uses React Query for data fetching and caching
|
|
- Follows shadcn/ui component patterns
|
|
- Implements Path Aliases with `@/` prefix for cleaner imports
|
|
- Uses Vite for fast development and production builds
|
|
- Component-based architecture with React hooks
|
|
- Default connection to multiple Nostr relays for network redundancy
|
|
|
|
## Build & Deployment
|
|
|
|
- Build for production: `npm run build`
|
|
- Development build: `npm run build:dev`
|
|
|
|
## Testing Your Changes
|
|
|
|
Use `npm run build` to check for build errors. |