From 5f4d5a52e885f048c51ba7e849e84d4e3c98f576 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sun, 29 Mar 2026 11:43:46 -0400 Subject: [PATCH] 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). --- package.json | 5 +- .../01-analyze-current-architecture.md | 623 ++++++++++++++++++ 2 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 tasks/native-business-logic-migration/01-analyze-current-architecture.md diff --git a/package.json b/package.json index 98c4b54..e95edd9 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/tasks/native-business-logic-migration/01-analyze-current-architecture.md b/tasks/native-business-logic-migration/01-analyze-current-architecture.md new file mode 100644 index 0000000..b665c52 --- /dev/null +++ b/tasks/native-business-logic-migration/01-analyze-current-architecture.md @@ -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 + +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 +├── updateSubscription(id, updates) → void +├── removeSubscription(id) → Promise +├── loadSubscriptions() → Promise +├── loadFeedItems(subscriptionId?, limit?, offset?) → Promise +├── 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 + +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 +├── loadHistory() → Promise +├── clearHistory() → Promise +├── removeFromHistory(query) → Promise +└── 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 \ No newline at end of file