Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 48 additions & 18 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@ Agents should prioritize backwards compatibility, API stability, and high test c
### Project layout (monorepo)

packages/
feeds-client/ # Core Feeds API client
src/
common/ # Common utilities (API client, state management, real-time)
feed/ # Feed management and event handlers
feeds-client/ # Main FeedsClient class
gen/ # Generated API clients and models
utils/ # Utility functions
types.ts # Type definitions
@react-bindings/ # React hooks and contexts
react-sdk/ # React SDK wrapper
react-native-sdk/ # React Native SDK wrapper
feeds-client/ # Core Feeds API client
src/
activity-with-state-updates/ # Activity state management
bindings/ # Framework bindings
common/ # Common utilities (API client, state management, real-time)
feed/ # Feed management and event handlers
feeds-client/ # Main FeedsClient class
gen/ # Generated API clients and models
utils/ # Utility functions
react-sdk/ # React SDK wrapper with hooks and contexts
react-native-sdk/ # React Native SDK wrapper
sample-apps/
react-sample-app/ # Next.js sample application for React
react-native/ # React Native sample application
react-demo/ # Next.js demo application (stream-feeds-react-demo)
react-sample-app/ # Next.js sample application (facebook-clone)
react-native/ # React Native sample application (ExpoTikTokApp)

Use the closest folder's patterns and conventions when editing.

Expand Down Expand Up @@ -148,11 +149,11 @@ Commit / PR conventions
Testing policy
• Add/extend tests in each package's test directories with .test.ts suffix.
• Cover:
• Core FeedsClient and Feed classes
• Event handlers and state management - see ai-docs/ai-state-management for details
• React hooks and contexts (@react-bindings)
• Utility functions (token creation, rate limiting, search)
• Generated API clients and their interactions
• Core FeedsClient and Feed classes
• Event handlers and state management - see ai-docs/ai-state-management for details
• React hooks and contexts (react-sdk, react-native-sdk)
• Utility functions (token creation, rate limiting, search)
• Generated API clients and their interactions
• Integration tests are in `__integration-tests__/` directories

Security & credentials
Expand Down Expand Up @@ -184,3 +185,32 @@ Quick agent checklist (per commit)
• Test affected packages individually if needed

End of machine guidance. Edit this file to refine agent behavior over time; keep human-facing details in README.md and docs.

## React Demo app

### Purpose

This is a React demo application showcasing the Stream Feeds SDK. Both source code quality and visual design should be excellent—this app serves as a reference implementation.

### UI Framework

This project uses **Tailwind CSS** with **daisyUI** for styling.

#### daisyUI Setup for Cursor

To get accurate daisyUI code generation, use one of these methods:

**Quick use in chat:**
```
@web https://daisyui.com/llms.txt
```

### Stream Feeds SDK Documentation

If something is not clear, ask for a documentation link

### Quality Standards

- **Source code**: Clean, well-structured, following React best practices
- **Design**: Modern, polished UI using daisyUI components effectively
- **Both must be excellent**—this is a showcase application
121 changes: 121 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build Commands

```bash
# Install dependencies (run from repository root)
yarn

# Build all packages
yarn build:all

# Build specific packages
yarn build:client # Build feeds-client only
yarn build:react-sdk # Build React SDK only
yarn build:react-native-sdk # Build React Native SDK only

# Development mode with watch (in packages/feeds-client)
yarn start
```

## Testing

```bash
# Run all tests
yarn test:ci:all

# Run tests for library packages only (no sample apps)
yarn test:ci:libs

# Run tests for feeds-client (in packages/feeds-client)
yarn test # Run all tests with vitest
yarn test:unit # Run unit tests only (excludes integration tests)
yarn test <pattern> # Run specific test file, e.g., yarn test feed.test

# Run a single test file
yarn vitest run path/to/test.test.ts

# Integration tests require environment variables:
# VITE_STREAM_API_KEY and VITE_STREAM_API_SECRET (see __integration-tests__/utils.ts)
```

## Linting

```bash
yarn lint:all # Lint all TypeScript files
yarn lint:all:fix # Lint and auto-fix
```

## Verification After Changes

**IMPORTANT**: After making code changes, always verify the build and lint status:

```bash
yarn build:all # Ensure all packages build successfully
yarn lint:all # Check for linting errors
```

These commands should be run from the repository root before considering changes complete.

## Code Generation

```bash
yarn generate # Regenerate OpenAPI types (runs generate-openapi.sh)
yarn lint:gen # Lint and format generated code
```

## Architecture

This is a Yarn 4 monorepo with three main packages and sample applications.

### Package Hierarchy

```
@stream-io/feeds-client (packages/feeds-client)
└── @stream-io/feeds-react-sdk (packages/react-sdk) - re-exports feeds-client
└── @stream-io/feeds-react-native-sdk (packages/react-native-sdk) - re-exports feeds-client
```

### feeds-client Structure

The core SDK with two entry points:
- Main entry (`index.ts`): `FeedsClient`, `Feed`, state management, types
- React bindings entry (`/react-bindings`): hooks, contexts, wrapper components

Key classes:
- **FeedsClient** (`src/feeds-client/feeds-client.ts`): Main client for API communication, WebSocket connections, and state management. Extends auto-generated `FeedsApi`.
- **Feed** (`src/feed/feed.ts`): Represents a single feed with its state. Extends auto-generated `FeedApi`.
- **StateStore**: From `@stream-io/state-store`, manages reactive state for both client and feeds.

### Generated Code

`src/gen/` contains OpenAPI-generated code:
- `models/`: API request/response types
- `feeds/`: `FeedsApi` and `FeedApi` base classes
- `model-decoders/`: WebSocket event decoders

### React Bindings

Located in `src/bindings/react/`:
- **Contexts**: `StreamFeedsContext`, `StreamFeedContext`, `StreamActivityWithStateUpdatesContext`, `StreamSearchContext`
- **Hooks**: `useCreateFeedsClient`, client/feed/search state hooks
- **Wrappers**: `StreamFeeds`, `StreamFeed`, `StreamActivityWithStateUpdates`, `StreamSearch`

### Event Handling

WebSocket events are processed through handlers in `src/feed/event-handlers/`. Each event type (activity, comment, follow, bookmark, etc.) has dedicated handler files that update the appropriate state stores.

### Sample Apps

- `sample-apps/react-demo`: Next.js demo app with stories feature with DaisyUI and Tailwind
- `sample-apps/react-sample-app`: Advanced Next.js example
- `sample-apps/react-native/ExpoTikTokApp`: Expo React Native example

## Key Patterns

- State management uses `@stream-io/state-store` with React bindings via `useSyncExternalStore`
- API types are generated from OpenAPI spec - don't manually edit files in `src/gen/`
- Integration tests in `__integration-tests__/` require Stream API credentials
- Tests use vitest; configuration is in `vite.config.ts`
81 changes: 66 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ Here are some of the features we support:
- **Search & queries**: Activity search, **query activities**, and **query feeds** endpoints.
- **Modern essentials**: Permissions • OpenAPI spec • GDPR endpoints • realtime WebSocket events • push notifications • “own capabilities” API.

## React sample apps

### React demo app with stories
## React demo app

Deployed version: https://feeds-react-demo.vercel.app

Expand Down Expand Up @@ -80,27 +78,80 @@ After the above steps run the following command in `sample-apps/react-demo`:
yarn dev
```

### Advanced React app
## Test Data Generator

Prerequisites:
The `test-data-generator` directory contains scripts to populate your Stream Feeds app with sample data for testing and development purposes.

- Install dependencies: `yarn`
- Build React SDK: `yarn build:client` and `yarn build:react-sdk`
- Create a `.env` file in `sample-apps/react-sample-app` with the following content:
### Setup

1. Create a `.env` file in `test-data-generator/` with your credentials:

```
NEXT_PUBLIC_STREAM_API_KEY=<Your API key>
NEXT_API_SECRET=<Your API secret>
NEXT_PUBLIC_API_URL=<Optionally provide an API URL>
STREAM_API_KEY=<Stream API key>
API_SECRET=<Stream API secret>
API_URL=<Optional, Stream API URL>
```

- Run the `node setup-env.js` script in `sample-apps/react-sample-app`
- If you want to have some pre-made posts in your app, optinally run the `node create-posts.js` script as well
2. Install dependencies: `yarn` (from the repository root)

### Available Scripts

After the above steps run the following command in `sample-apps/react-sample-app`:
Run these commands from the `test-data-generator/` directory:

| Script | Command | Description |
| --------------- | ---------------------- | ------------------------------------------ |
| Create Users | `yarn create-users` | Creates users and their feeds |
| Create Follows | `yarn create-follows` | Sets up follow relationships between users |
| Create Posts | `yarn create-posts` | Generates sample activities/posts |
| Create Stories | `yarn create-stories` | Creates sample stories |
| Download Images | `yarn download-images` | Downloads sample images for posts |

### Create Posts Feature Flags

The `create-posts` script supports a `--features` flag to control which features are included in the generated posts:

```bash
yarn create-posts --features <feature1,feature2,...>
```
yarn dev

**Available features:**

| Feature | Description |
| ------------ | ---------------------------------------- |
| `link` | Adds random URLs to posts |
| `attachment` | Uploads and attaches 1-3 images to posts |
| `mention` | Adds @mentions to other users |
| `poll` | Creates and attaches polls to posts |
| `reaction` | Adds 1-5 reactions from random users |
| `comment` | Adds 1-5 comments from random users |
| `bookmark` | Bookmarks posts by random users |
| `repost` | Creates reposts of existing activities |

**Examples:**

```bash
# Create basic posts without any features
yarn create-posts

# Create posts with polls and reactions
yarn create-posts --features poll,reaction

# Create posts with all content features
yarn create-posts --features link,attachment,mention,poll,reaction,comment,bookmark,repost
```

> Note: Each feature has a probability of being included (not every post will have every enabled feature). Link and attachment are mutually exclusive per post.

### Usage

Typical order of operations:

```bash
cd test-data-generator
yarn create-users
yarn create-follows
yarn create-posts --features link,attachment,mention,poll,reaction,comment,bookmark,repost
yarn create-stories
```

## Local Setup
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"license": "See license in LICENSE",
"workspaces": [
"packages/*",
"sample-apps/**"
"sample-apps/**",
"test-data-generator"
],
"scripts": {
"build:all": "yarn workspaces foreach -Avp --topological-dev run build",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ describe('Activity state updates via WebSocket events', () => {
text: 'Test activity',
});

await waitForEvent(feed, 'feeds.activity.added', { timeoutMs: 1000 });
await waitForEvent(feed, 'feeds.activity.added', { timeoutMs: 10000 });

expect(
feed.state
Expand Down
2 changes: 1 addition & 1 deletion packages/feeds-client/__integration-tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const waitForEvent = (
client: FeedsClient | Feed,
type: FeedsEvent['type'] | WSEvent['type'],
{
timeoutMs = 3000,
timeoutMs = 10000,
shouldReject = false,
}: {
timeoutMs?: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createContext, useContext } from 'react';

import type { ActivityWithStateUpdates } from '../../../activity-with-state-updates/activity-with-state-updates';

export const StreamActivityWithStateUpdatesContext = createContext<ActivityWithStateUpdates | undefined>(undefined);

/**
* The props for the StreamActivityWithStateUpdatesProvider component.
*/
export type StreamActivityWithStateUpdatesContextProps = {
activityWithStateUpdates: ActivityWithStateUpdates;
};

/**
* Hook to access the nearest ActivityWithStateUpdates instance.
*/
export const useActivityWithStateUpdatesContext = () => {
return useContext(StreamActivityWithStateUpdatesContext);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { checkHasAnotherPage } from '../../../../utils';
import type { Feed, FeedState } from '../../../../feed';
import type { ActivityState, ActivityWithStateUpdates } from '../../../../activity-with-state-updates/activity-with-state-updates';
import type { ActivityResponse, CommentResponse } from '../../../../gen/models';
import { useActivityWithStateUpdatesContext } from '../../contexts/StreamActivityWithStateUpdatesContext';

const canLoadComments = (
feedOrActivity: Feed | ActivityResponse | ActivityWithStateUpdates,
Expand Down Expand Up @@ -36,15 +37,17 @@ type UseCommentsReturnType<T extends ActivityResponse | CommentResponse> = {
export function useActivityComments({
feed: feedFromProps,
parentComment,
activity,
activity: activityFromProps,
}: {
feed?: Feed;
parentComment?: CommentResponse;
activity?: ActivityResponse | ActivityWithStateUpdates;
}) {
} = {}) {
const feedFromContext = useFeedContext();
const feed = feedFromProps ?? feedFromContext;
const feedOrActivity = feed ?? activity;
const activityFromContext = useActivityWithStateUpdatesContext();
const activity = activityFromProps ?? activityFromContext;
const feedOrActivity = (activity && canLoadComments(activity)) ? activity : feed;

if (!feedOrActivity) {
throw new Error('Feed or activity is required');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const useCreateFeedsClient = ({

const connectionPromise = cachedUserData
? _client
.connectUser(cachedUserData, tokenOrProvider)
.connectUser(cachedUserData, tokenOrProvider!)
.then(() => {
setError(null);
})
Expand Down
Loading
Loading