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
41 changes: 26 additions & 15 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,40 @@
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"plugins": [
"@typescript-eslint"
],
"rules": {
"no-console": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["warn", {
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}],
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/prefer-nullish-coalescing": "off",
"@typescript-eslint/prefer-optional-chain": "warn",
"@typescript-eslint/no-floating-promises": "warn",
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/no-misused-promises": ["warn", {
"checksVoidReturn": false
}],
"react-hooks/exhaustive-deps": "warn"
"@typescript-eslint/prefer-optional-chain": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksVoidReturn": false
}
],
"react-hooks/exhaustive-deps": "error",
"@next/next/no-img-element": "off"
},
"overrides": [
{
"files": ["lib/logger.ts"],
"files": [
"lib/logger.ts"
],
"rules": {
"no-console": "off"
}
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,47 @@ jobs:
- name: Run TypeScript compiler
run: npx tsc --noEmit

unit:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run Vitest
run: npm run test

dead-code:
name: Dead Code (knip)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

# Unused files and dependencies fail the job. Unused exports are reported
# but not yet enforced: the backlog is being worked down and a clean
# `knip` run is the target state before this becomes strict.
- name: Run knip (files + dependencies)
run: npx knip --include files,dependencies,unlisted

build:
name: Build
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ lib/wasm-sdk/

# Keep only the necessary WASM files in dash-wasm
# (the actual SDK files we need are in lib/dash-wasm/)
.gitignore worktrees
worktrees/

# devnet seeding ledgers (private keys + checkpoints) — never commit
.seed-treasury.local.key
Expand Down
27 changes: 19 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Development Commands

```bash
npm run dev # Start development server
npm run build # Build for production
npm run lint # Run linting
npm run dev # Start development server
npm run build # Build for production
npm run lint # ESLint over app/ components/ contexts/ hooks/ lib/ types/; warnings fail
npm run test # Vitest unit tests (lib/**/*.test.ts)
npm run lint:dead # knip: unused files, exports, and dependencies
```

## Workflow
Expand All @@ -25,7 +27,14 @@ npm run lint
- Fix all errors and warnings properly (see Code Quality Guidelines below)
- Do not commit code with linter failures

### 2. Run the Build
### 2. Run the Unit Tests
```bash
npm run test
```
- Pure modules under `lib/` (crypto primitives, codecs, parsers) have Vitest specs next to them as `*.test.ts`
- Add or extend a spec when touching one of these modules; anything that needs the SDK or a browser belongs in `e2e/`

### 3. Run the Build
```bash
npm run build
```
Expand All @@ -34,7 +43,7 @@ npm run build
- Catches missing imports, type errors, and build-time issues
- Do not commit code that fails to build

### 3. Run the End-to-End Tests
### 4. Run the End-to-End Tests
```bash
npm run build:testing # build the /testing bundle the tests run against
npm run test:e2e
Expand All @@ -44,7 +53,7 @@ npm run test:e2e
- The full suite needs `E2E_SEED_PHRASE` (in gitignored `.env.local`) and performs real state transitions against the dedicated test contracts in `.env.testing` — never production
- See `docs/TESTING.md` for the identity pool, provisioning, and known quirks

### 4. Code Review for Complex Changes
### 5. Code Review for Complex Changes
For complex or multi-file changes, use a code review sub-agent to identify potential issues:

```
Expand All @@ -58,7 +67,7 @@ Use the Task tool with subagent_type=Plan to review the changes for:

**Trust but verify**: The review agent may flag potential issues that aren't actually problems, or miss real issues. Treat its output as suggestions to investigate, not definitive judgments. Verify each finding before acting on it.

### 5. Manual Verification (when applicable)
### 6. Manual Verification (when applicable)
- For UI changes: Run `npm run dev` and visually verify the changes
- For new features: Test the happy path and common error cases
- For bug fixes: Confirm the original issue is resolved
Expand All @@ -67,7 +76,9 @@ Use the Task tool with subagent_type=Plan to review the changes for:
| Check | Command | Required |
|-------|---------|----------|
| Linter | `npm run lint` | Always |
| Unit tests | `npm run test` | Always |
| Build | `npm run build` | Always |
| Dead code | `npm run lint:dead` | When adding or removing modules/exports |
| End-to-End | `npm run build:testing && npm run test:e2e` | Changes touching feeds, posts, or auth flows |
| Code Review | Task sub-agent | Complex changes |
| Dev Server | `npm run dev` | UI changes |
Expand Down Expand Up @@ -167,7 +178,7 @@ Additional contracts back specific features: storefront (7 types), DM, blog, vau
1. **State Management**: Zustand store in `lib/store.ts`
2. **Styling**: Tailwind CSS with custom design system in `tailwind.config.js`
3. **UI Components**: Radix UI primitives in `components/ui/`
4. **Mock Data**: `lib/mock-data.ts` for development when not connected to Dash Platform
4. **Default avatars**: `lib/mock-data.ts` generates the DiceBear placeholder used when a profile has no avatar

### Known Issues

Expand Down
21 changes: 8 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A decentralized social media platform and marketplace built on Dash Platform. All data—posts, profiles, likes, follows, bookmarks, mentions, tips, direct messages, stores, and orders—is stored on-chain with full user ownership.

<img src="assets/yappr.png" alt="Yappr Screenshot" width="200">
<img src="public/yappr.png" alt="Yappr Screenshot" width="200">

## Features

Expand Down Expand Up @@ -93,8 +93,8 @@ npm run dev
# Build for production
npm run build

# Build for GitHub Pages
npm run build:gh-pages
# Build for a sub-path deployment (e.g. GitHub Pages)
npm run build:subpath

# Run linting
npm run lint
Expand Down Expand Up @@ -127,7 +127,7 @@ yappr/
│ ├── orders/seller/ # Seller order management
│ ├── post/ # Post detail view and threads
│ ├── privacy/ # Privacy policy
│ ├── profile/ # User profile (current user + edit)
│ ├── profile/create/ # Profile creation
│ ├── search/ # Search users and hashtags
│ ├── settings/ # User settings
│ ├── store/ # Store listing and storefront views
Expand Down Expand Up @@ -171,13 +171,11 @@ yappr/
│ ├── use-link-preview.ts # Link preview fetching
│ ├── use-login-prompt-modal.ts # Login prompt modal
│ ├── use-mention-validation.ts # Mention validation
│ ├── use-platform-detection.ts # Platform/device detection
│ ├── use-post-detail.ts # Post detail with thread loading
│ ├── use-post-enrichment.ts # Post stats with deduplication
│ ├── use-private-feed-request.ts # Private feed access requests
│ ├── use-progressive-enrichment.ts # Progressive data loading
│ ├── use-require-auth.ts # Auth requirement wrapper
│ ├── use-require-encryption-key.ts # Require encryption key
│ └── use-tip-modal.ts # Tip/payment modal
├── lib/
Expand Down Expand Up @@ -206,7 +204,6 @@ yappr/
│ │ ├── private-feed-follower-service.ts # Private feed grants
│ │ ├── private-feed-crypto-service.ts # Private feed encryption
│ │ ├── private-feed-key-store.ts # Private feed key storage
│ │ ├── profile-migration-service.ts # Profile migration
│ │ ├── profile-service.ts # Profile management
│ │ ├── reply-service.ts # Reply operations
│ │ ├── repost-service.ts # Reposts
Expand Down Expand Up @@ -234,20 +231,18 @@ yappr/
│ ├── bloom-filter.ts # Bloom filter for efficient lookups
│ ├── cache-manager.ts # Query caching
│ ├── constants.ts # Contract IDs, network config
│ ├── dash-platform-client.ts # Platform client wrapper
│ ├── error-utils.ts # Error handling utilities
│ ├── message-encryption.ts # DM encryption
│ ├── mock-data.ts # Development mock data
│ ├── mock-data.ts # Default placeholder avatar
│ ├── onchain-key-encryption.ts # Key backup encryption
│ ├── post-helpers.ts # Post utility functions
│ ├── retry-utils.ts # Retry logic with backoff
│ ├── secure-storage.ts # Session storage for keys
│ ├── store.ts # Main Zustand store
│ ├── types.ts # TypeScript interfaces
│ └── utils.ts # Helper functions
│ ├── types.ts # TypeScript interfaces (re-exports types/)
│ └── utils/ # Helper functions
├── types/
│ └── sdk.ts # Dash SDK type definitions
├── types/ # Domain types: user, post, store, notification
├── contracts/ # Dash Platform data contracts
│ ├── yappr-social-contract-v2.json # Main social contract (staging/prod)
Expand Down
5 changes: 2 additions & 3 deletions app/contract/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,8 @@ export default function ContractPage() {
}

const documentCount = Object.keys(dataContract.documents).length
const totalIndices = Object.values(dataContract.documents).reduce((acc, doc: any) =>
acc + (doc.indices?.length || 0), 0
)
const totalIndices = Object.values(dataContract.documents as Record<string, { indices?: unknown[] }>)
.reduce((acc, doc) => acc + (doc.indices?.length || 0), 0)

return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
Expand Down
51 changes: 13 additions & 38 deletions app/explore/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ import { TopCreators } from '@/components/explore/top-creators'
import type { Post, Blog, BlogPostWithAuthor } from '@/lib/types'
import { enrichBlogPostsWithAuthors, getBlogPostUrl } from '@/lib/blog/content-utils'

interface RawPostDocument {
$id: string
$ownerId: string
$createdAt: number
content?: string
}

type ExploreTab = 'hashtags' | 'top' | 'creators' | 'blogs'

export default function ExplorePage() {
Expand Down Expand Up @@ -156,45 +149,27 @@ export default function ExplorePage() {
try {
setIsSearching(true)

// Search regular posts
const { getDashPlatformClient } = await import('@/lib/dash-platform-client')
const dashClient = getDashPlatformClient()

const allPosts = await dashClient.queryPosts({ limit: 100 })
// Search regular posts: a client-side substring match over the most
// recent timeline page. Authors are left as placeholders for PostCard
// to resolve progressively.
const { postService } = await import('@/lib/services/post-service')
const { documents: recentPosts } = await postService.getTimeline({ limit: 100 })

const typedPosts = allPosts as RawPostDocument[]
const authorIds = Array.from(new Set(typedPosts.map(p => p.$ownerId).filter(Boolean)))
const authorIds = Array.from(new Set(recentPosts.map(p => p.author.id).filter(Boolean)))
const blockedMap = user?.identityId
? await checkBlockedForAuthors(user.identityId, authorIds)
: new Map<string, boolean>()

const filtered = typedPosts
const needle = searchQuery.toLowerCase()
const filtered = recentPosts
.filter(post =>
post.$ownerId &&
post.content?.toLowerCase().includes(searchQuery.toLowerCase()) &&
!blockedMap.get(post.$ownerId)
!post.deleted &&
post.content.toLowerCase().includes(needle) &&
!blockedMap.get(post.author.id)
)
.map(post => ({
id: post.$id,
content: post.content || '',
author: {
id: post.$ownerId,
username: '',
handle: '',
displayName: '',
avatar: '',
followers: 0,
following: 0,
verified: false,
joinedAt: new Date(),
hasDpns: undefined
},
createdAt: new Date(post.$createdAt || 0),
likes: 0,
replies: 0,
reposts: 0,
quotes: 0,
views: 0
...post,
author: { ...post.author, username: '', displayName: '', avatar: '', hasDpns: undefined },
}))

setSearchResults(filtered)
Expand Down
Loading
Loading