docs: Document current Expo architecture for native migration

- Document technology stack (Expo, TypeScript, Zustand, SQLite)
- Map out all data models and types
- Document service dependencies and architecture
- Create component dependency diagrams
- Document data flow for feed sync and search
- Provide migration checklist with priorities
- Identify pure TypeScript vs native-dependent services

This analysis provides the foundation for migrating business logic
from Expo/TypeScript to native platforms (iOS, Android, Linux).
This commit is contained in:
2026-03-29 11:43:46 -04:00
parent a7d4f4e4d3
commit 5f4d5a52e8
2 changed files with 627 additions and 1 deletions

View File

@@ -17,6 +17,8 @@
"@react-navigation/native": "^7.1.33",
"@tanstack/react-query": "^5.95.2",
"axios": "^1.14.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"expo": "55.0.10-canary-20260328-2049187",
"expo-constants": "55.0.10-canary-20260328-2049187",
"expo-device": "55.0.11-canary-20260328-2049187",
@@ -34,6 +36,7 @@
"expo-system-ui": "55.0.12-canary-20260328-2049187",
"expo-task-manager": "55.0.11-canary-20260328-2049187",
"expo-web-browser": "55.0.11-canary-20260328-2049187",
"fast-xml-parser": "^5.5.9",
"react": "19.2.0",
"react-dom": "19.2.0",
"react-native": "0.83.4",
@@ -44,7 +47,7 @@
"react-native-screens": "~4.23.0",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.7.2",
"xml2js": "^0.6.2",
"stream-browserify": "^3.0.0",
"zustand": "^5.0.12"
},
"devDependencies": {

View File

@@ -0,0 +1,623 @@
# 01. Analyze and document current Expo architecture
meta:
id: native-business-logic-migration-01
feature: native-business-logic-migration
priority: P0
depends_on: []
tags: [analysis, documentation]
objective:
- Document the current Expo/TypeScript architecture to understand what needs to be migrated
deliverables:
- Architecture documentation file
- Component dependency diagram
- Data flow diagrams
- List of all services, stores, and utilities to migrate
steps:
- Review all TypeScript source files in src/
- Identify all data models and types
- Map out service dependencies
- Document state management flow (Zustand stores)
- Identify database schema and queries
- Document API endpoints and network calls
- Create migration checklist
acceptance_criteria:
- Architecture document exists with all components listed
- Data flow diagrams created for key features
- Migration checklist complete with all items identified
- All services and their dependencies documented
notes:
- Focus on business logic, not UI components
- Pay attention to async operations and error handling
- Document any platform-specific code that needs special handling
---
# Current Architecture Analysis
## Overview
RSSuper is a multi-platform RSS reader built with **Expo (React Native)** using **TypeScript**. The current architecture follows a service-oriented pattern with Zustand for state management and expo-sqlite for local persistence.
## Technology Stack
| Layer | Technology |
|-------|------------|
| Framework | Expo SDK 55 (canary) |
| Runtime | React Native 0.83.4 |
| Language | TypeScript 5.9 |
| State Management | Zustand 5.x |
| Data Fetching | TanStack Query 5.x |
| HTTP Client | Axios |
| Local Database | expo-sqlite |
| Navigation | React Navigation 7.x |
| XML Parsing | fast-xml-parser |
| Audio | expo-av |
---
## Data Models
### Core Types (`src/types/feed.ts`)
```
FeedItem
├── id: string
├── title: string
├── link?: string
├── description?: string
├── content?: string
├── author?: string
├── published?: Date
├── updated?: Date
├── categories?: string[]
├── enclosure?: { url, type, length? }
└── guid?: string
Feed
├── id: string
├── title: string
├── link?: string
├── description?: string
├── subtitle?: string
├── language?: string
├── lastBuildDate?: Date
├── updated?: Date
├── generator?: string
├── ttl?: number
├── items: FeedItem[]
├── rawUrl: string
├── lastFetchedAt?: Date
└── nextFetchAt?: Date
FeedSubscription
├── id: string
├── url: string
├── title: string
├── category?: string
├── enabled: boolean
├── fetchInterval: number (minutes)
├── createdAt: Date
├── updatedAt: Date
├── lastFetchedAt?: Date
├── nextFetchAt?: Date
├── error?: string
└── httpAuth?: { username, password }
SearchQuery / SearchFilters / SearchResult
```
### Global Types (`src/types/global.d.ts`)
```
NotificationType (enum)
├── NEW_ARTICLE
├── EPISODE_RELEASE
├── CUSTOM_ALERT
└── UPGRADE_PROMO
NotificationConfig
AccountSettings
PushNotificationConfig
```
---
## Services Architecture
### 1. Feed Service (`src/services/feed-service.ts`)
**Responsibilities**: RSS/Atom feed parsing and fetching
```
Functions:
├── parseFeed(url, data) → ParseResult
├── fetchFeed(url, auth?) → ParseResult
└── fetchFeeds(subscriptions) → Map<string, ParseResult>
Dependencies:
├── axios (HTTP)
└── xml2js (XML parsing)
Platform: Pure TypeScript - NO native dependencies
```
### 2. Database Service (`src/services/database.ts`)
**Responsibilities**: SQLite persistence layer
```
Tables:
├── subscriptions
│ ├── id (PRIMARY KEY)
│ ├── url (UNIQUE)
│ ├── title, category
│ ├── enabled, fetch_interval
│ ├── http_auth_username, http_auth_password
│ ├── timestamps (created_at, updated_at, last_fetched_at, next_fetch_at)
│ └── error
├── feeds
│ ├── id (PRIMARY KEY)
│ ├── subscription_id (FOREIGN KEY)
│ ├── title, link, description, author
│ ├── published, updated, content
│ ├── guid (UNIQUE)
│ └── created_at
├── feeds_fts (FTS5 virtual table)
├── subscriptions_fts (FTS5 virtual table)
└── search_history
Functions:
├── initDatabase() → void
├── saveSubscription(sub) → void
├── getSubscription(id) → FeedSubscription
├── getAllSubscriptions() → FeedSubscription[]
├── deleteSubscription(id) → void
├── saveFeedItems(subscriptionId, items) → void
├── getFeedItems(subscriptionId?, limit, offset) → FeedItem[]
├── getAllFeedItems(limit, offset) → FeedItem[]
└── search history functions
Dependencies: expo-sqlite
```
### 3. Search Service (`src/services/search-service.ts`)
**Responsibilities**: Full-text search using SQLite FTS5
```
Functions:
├── searchArticles(query, options) → SearchResult[]
├── searchFeeds(query, limit) → SearchResult[]
└── combinedSearch(query, options) → { articles, feeds }
Search Features:
├── FTS5 full-text search (primary)
├── LIKE fallback search
├── Date filters
├── Feed filters
├── Author filters
├── Content type filters
└── Multiple sort options
Dependencies: database service
```
### 4. Sync Service (`src/services/sync-service.ts`)
**Responsibilities**: Feed synchronization orchestration
```
Functions:
├── syncFeed(subscription) → { success, itemsSynced }
├── syncAllFeeds() → { success, totalItemsSynced }
├── getFeedsDueForSync() → FeedSubscription[]
├── getLocalFeedItems(...) → FeedItem[]
├── getAllLocalFeedItems(limit) → FeedItem[]
├── hasLocalData() → boolean
└── resolveConflict(options) → FeedItem
Conflict Resolution Strategies:
├── newer
├── older
├── local
└── remote
Dependencies: feed-service, database, feed-store
```
### 5. Background Sync Service (`src/services/background-sync.ts`)
**Responsibilities**: Background task management
```
Task: BACKGROUND_SYNC_TASK
├── Uses expo-task-manager
├── Registers background fetch task
└── Syncs all enabled feeds
Functions:
├── registerBackgroundSync() → boolean
├── unregisterBackgroundSync() → void
└── isBackgroundSyncRegistered() → boolean
Dependencies: expo-task-manager, sync-service, feed-store
```
### 6. Notification Service (`src/services/notification-service.ts`)
**Responsibilities**: Push and local notifications
```
Functions:
├── registerNotificationChannels() → void (Android)
├── requestNotificationPermissions() → boolean
├── getPermissionStatus() → status
├── scheduleNotification(config) → string | null
├── scheduleNotificationAtTime(config, date) → string | null
├── cancelNotification(id) → void
├── cancelAllNotifications() → void
├── getScheduledNotifications() → NotificationRequest[]
├── showNotification(config) → void
├── setBadgeCount(count) → void
├── getBadgeCount() → number
├── addNotificationListener(callback) → unsubscribe
└── addNotificationResponseListener(callback) → unsubscribe
Android Channels:
├── default
├── new-articles
├── episode-releases
└── custom-alerts
Dependencies: expo-notifications, settings-store
```
### 7. Audio Player Service (`src/services/audio-player.ts`)
**Responsibilities**: Podcast/audio playback
```
Class: AudioPlayerService
├── initialize() → void
├── subscribe(callback) → unsubscribe
├── play(item) → void
├── pause() → void
├── resume() → void
├── stop() → void
├── seekTo(positionMs) → void
├── setPlaybackSpeed(speed) → void
├── skipForward(seconds) → void
├── skipBackward(seconds) → void
└── getState() → PlayerState
Playback States: idle | loading | playing | paused | error
Playback Speeds: 0.5 | 0.75 | 1 | 1.25 | 1.5 | 1.75 | 2
Dependencies: expo-av
```
### 8. API Service (`src/services/api.ts`)
**Responsibilities**: HTTP client setup (currently minimal)
```
Setup:
├── Axios instance with baseURL
├── Request interceptor (auth token placeholder)
└── Response interceptor (error handling)
Note: Not actively used - TODO for future cloud sync
```
---
## State Management (Zustand Stores)
### 1. Feed Store (`src/stores/feed-store.ts`)
```
State:
├── subscriptions: FeedSubscription[]
├── feedItems: FeedItem[]
├── loading: boolean
└── error: string | null
Actions:
├── addSubscription(subscription) → Promise<void>
├── updateSubscription(id, updates) → void
├── removeSubscription(id) → Promise<void>
├── loadSubscriptions() → Promise<void>
├── loadFeedItems(subscriptionId?, limit?, offset?) → Promise<void>
├── setLoading(loading) → void
├── setError(error) → void
└── clearError() → void
```
### 2. Settings Store (`src/stores/settings-store.ts`)
```
State:
├── syncInterval: SyncInterval
├── theme: 'system' | 'light' | 'dark'
├── notificationPreferences: NotificationPreferences
├── readingPreferences: ReadingPreferences
└── accountSettings: AccountSettings
Actions:
├── setSyncInterval(interval) → void
├── setTheme(theme) → void
├── setNotificationPreferences(prefs) → void
├── setReadingPreferences(prefs) → void
├── setAccountSettings(settings) → void
└── resetSettings() → void
Persistence: AsyncStorage (via zustand persist)
Storage Key: rssuper-settings
```
### 3. Bookmark Store (`src/stores/bookmark-store.ts`)
```
State:
└── bookmarkedIds: Set<string>
Actions:
├── addBookmark(articleId) → void
├── removeBookmark(articleId) → void
├── toggleBookmark(articleId) → void
├── isBookmarked(articleId) → boolean
└── getBookmarks() → string[]
Persistence: AsyncStorage (via zustand persist)
Storage Key: rssuper-bookmarks
```
### 4. Search Store (`src/stores/search-store.ts`)
```
State:
├── query: string
├── filters: SearchFilters
├── sort: SearchSortOption
├── page: number
├── pageSize: number
├── loading: boolean
├── error: string | null
└── searchHistory: SearchHistoryItem[]
Actions:
├── setQuery(query) → void
├── setFilters(filters) → void
├── setSort(sort) → void
├── setPage(page) → void
├── setLoading(loading) → void
├── setError(error) → void
├── clearSearch() → void
├── addToHistory(query) → Promise<void>
├── loadHistory() → Promise<void>
├── clearHistory() → Promise<void>
├── removeFromHistory(query) → Promise<void>
└── resetSearch() → void
```
---
## Component Dependencies
```
┌─────────────────────────────────────────────────────────────────────────┐
│ UI Components │
│ (feed-item-card, article-card, search-filters, etc.) │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ React Hooks │
│ use-feed-list, use-theme, use-color-scheme, use-offline │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Zustand Stores │
│ feed-store, settings-store, bookmark-store, search-store │
└─────────────────────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Services Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │feed-service │ │search-service│ │ sync-service │ │ api.ts │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └───────────┘ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │background-sync.ts │ │notification-service │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Data Layer │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ database.ts (expo-sqlite) │ │
│ │ - subscriptions table │ │
│ │ - feeds table │ │
│ │ - FTS5 virtual tables │ │
│ │ - search_history table │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Expo Modules (Native) │
│ expo-sqlite, expo-task-manager, expo-notifications, expo-av │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Data Flow Diagrams
### Feed Sync Flow
```
User adds subscription
┌───────────────────┐
│ feed-store │
│ addSubscription() │
└─────────┬─────────┘
┌───────────────────┐ ┌───────────────────┐
│ database.ts │ │ settings-store │
│ saveSubscription()│ │ (check intervals) │
└─────────┬─────────┘ └───────────────────┘
▼ (manual or background)
┌───────────────────┐
│ sync-service │
│ syncFeed() │
└─────────┬─────────┘
┌───────────────────┐ ┌───────────────────┐
│ feed-service │ │ feed-store │
│ fetchFeed() │ │ updateSubscription│
│ parseFeed() │ └─────────┬─────────┘
└─────────┬─────────┘ │
│ ▼
│ ┌───────────────────┐
│ │ database.ts │
│ │ saveFeedItems() │
│ └─────────┬─────────┘
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ axios.get() │ │ UI updates │
│ (HTTP request) │ │ via Zustand │
└───────────────────┘ └───────────────────┘
```
### Search Flow
```
User enters search query
┌───────────────────┐
│ search-store │
│ setQuery() │
└─────────┬─────────┘
┌───────────────────┐
│ search-service │
│ searchArticles() │
└─────────┬─────────┘
┌───────────────────┐ ┌───────────────────┐
│ database.ts │ │ FTS5 or LIKE │
│ getDb() │────▶│ fallback search │
└─────────┬─────────┘ └───────────────────┘
┌───────────────────┐
│ SearchResult[] │
└─────────┬─────────┘
┌───────────────────┐
│ UI renders │
│ search-results │
└───────────────────┘
```
---
## Migration Checklist
### Services to Migrate (Pure TypeScript)
| Service | File | Native Deps | Priority |
|---------|------|-------------|----------|
| Feed Service | `feed-service.ts` | None | P0 |
| Search Service | `search-service.ts` | None | P0 |
| Sync Service | `sync-service.ts` | None | P0 |
| API Service | `api.ts` | None | P1 |
### Services with Native Dependencies
| Service | File | Native Deps | Priority |
|---------|------|-------------|----------|
| Database | `database.ts` | expo-sqlite | P0 |
| Background Sync | `background-sync.ts` | expo-task-manager | P0 |
| Notifications | `notification-service.ts` | expo-notifications | P0 |
| Audio Player | `audio-player.ts` | expo-av | P1 |
### Stores to Migrate
| Store | File | Persistence | Priority |
|-------|------|-------------|----------|
| Feed Store | `feed-store.ts` | None | P0 |
| Settings Store | `settings-store.ts` | AsyncStorage | P0 |
| Bookmark Store | `bookmark-store.ts` | AsyncStorage | P0 |
| Search Store | `search-store.ts` | None | P0 |
### Types to Migrate
| Types | File | Priority |
|-------|------|----------|
| Feed Types | `types/feed.ts` | P0 |
| Global Types | `types/global.d.ts` | P0 |
---
## Key Findings
1. **Pure TypeScript Services** (can be shared across all platforms):
- Feed parsing (RSS/Atom)
- Search logic
- Sync orchestration
- Conflict resolution
2. **Native Module Dependencies**:
- `expo-sqlite` → SQLite database
- `expo-task-manager` → Background tasks
- `expo-notifications` → Push/local notifications
- `expo-av` → Audio playback
3. **State Management**:
- All stores use Zustand
- Settings and bookmarks persist to AsyncStorage
- Database serves as source of truth for feeds/subscriptions
4. **Error Handling**:
- Most services return `{ success: boolean; error?: string }`
- Try-catch blocks with meaningful error messages
- Fallback strategies (e.g., FTS → LIKE search)
5. **Platform-Specific Code**:
- `hooks/use-color-scheme.web.ts` - Web-specific color scheme
- `components/animated-icon.web.tsx` - Web-specific icon animation
- `components/app-tabs.web.tsx` - Web-specific tabs
---
## Migration Priorities
1. **Phase 1**: Data models and types (shared)
2. **Phase 2**: Pure TypeScript services (feed, search, sync)
3. **Phase 3**: Database layer with expo-sqlite
4. **Phase 4**: Background sync with expo-task-manager
5. **Phase 5**: Notifications with expo-notifications
6. **Phase 6**: Audio player with expo-av
7. **Phase 7**: State management integration