···108 found_at TIMESTAMP DEFAULT NOW(),
109 last_verified TIMESTAMP,
110 is_active BOOLEAN DEFAULT TRUE,
00111 UNIQUE(source_account_id, atproto_did)
112 )
113 `;
···143 )
144 `;
145146- // ==================== ENHANCED INDEXES FOR PHASE 2 ====================
147-148 // Existing indexes
149 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_to_check ON source_accounts(source_platform, match_found, last_checked)`;
150 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_platform ON source_accounts(source_platform)`;
···156 await sql`CREATE INDEX IF NOT EXISTS idx_user_match_status_did_followed ON user_match_status(did, followed)`;
157 await sql`CREATE INDEX IF NOT EXISTS idx_notification_queue_pending ON notification_queue(sent, created_at) WHERE sent = false`;
158159- // NEW: Enhanced indexes for common query patterns
160161 // For sorting
162 await sql`CREATE INDEX IF NOT EXISTS idx_atproto_matches_stats ON atproto_matches(source_account_id, found_at DESC, post_count DESC, follower_count DESC)`;
···183184 // For bulk operations - normalized username lookups
185 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_normalized ON source_accounts(normalized_username, source_platform)`;
0000186187 console.log("✅ Database indexes created/verified");
188}
···108 found_at TIMESTAMP DEFAULT NOW(),
109 last_verified TIMESTAMP,
110 is_active BOOLEAN DEFAULT TRUE,
111+ follow_status JSONB DEFAULT '{}',
112+ last_follow_check TIMESTAMP,
113 UNIQUE(source_account_id, atproto_did)
114 )
115 `;
···145 )
146 `;
14700148 // Existing indexes
149 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_to_check ON source_accounts(source_platform, match_found, last_checked)`;
150 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_platform ON source_accounts(source_platform)`;
···156 await sql`CREATE INDEX IF NOT EXISTS idx_user_match_status_did_followed ON user_match_status(did, followed)`;
157 await sql`CREATE INDEX IF NOT EXISTS idx_notification_queue_pending ON notification_queue(sent, created_at) WHERE sent = false`;
158159+ // ======== Enhanced indexes for common query patterns =========
160161 // For sorting
162 await sql`CREATE INDEX IF NOT EXISTS idx_atproto_matches_stats ON atproto_matches(source_account_id, found_at DESC, post_count DESC, follower_count DESC)`;
···183184 // For bulk operations - normalized username lookups
185 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_normalized ON source_accounts(normalized_username, source_platform)`;
186+187+ // Follow status indexes
188+ await sql`CREATE INDEX IF NOT EXISTS idx_atproto_matches_follow_status ON atproto_matches USING gin(follow_status)`;
189+ await sql`CREATE INDEX IF NOT EXISTS idx_atproto_matches_follow_check ON atproto_matches(last_follow_check)`;
190191 console.log("✅ Database indexes created/verified");
192}
+17-14
netlify/functions/get-upload-details.ts
···82 // Fetch paginated results with optimized query
83 const results = await sql`
84 SELECT
85- sa.source_username,
86- sa.normalized_username,
87- usf.source_date,
88- am.atproto_did,
89- am.atproto_handle,
90- am.atproto_display_name,
91- am.atproto_avatar,
92- am.atproto_description,
93- am.match_score,
94- am.post_count,
95- am.follower_count,
96- am.found_at,
97- ums.followed,
98- ums.dismissed,
0099 -- Calculate if this is a new match (found after upload creation)
100 CASE WHEN am.found_at > uu.created_at THEN 1 ELSE 0 END as is_new_match
101 FROM user_source_follows usf
···153 foundAt: row.found_at,
154 followed: row.followed || false,
155 dismissed: row.dismissed || false,
0156 });
157 }
158 });
···82 // Fetch paginated results with optimized query
83 const results = await sql`
84 SELECT
85+ sa.source_username,
86+ sa.normalized_username,
87+ usf.source_date,
88+ am.atproto_did,
89+ am.atproto_handle,
90+ am.atproto_display_name,
91+ am.atproto_avatar,
92+ am.atproto_description,
93+ am.match_score,
94+ am.post_count,
95+ am.follower_count,
96+ am.found_at,
97+ am.follow_status,
98+ am.last_follow_check,
99+ ums.followed,
100+ ums.dismissed,
101 -- Calculate if this is a new match (found after upload creation)
102 CASE WHEN am.found_at > uu.created_at THEN 1 ELSE 0 END as is_new_match
103 FROM user_source_follows usf
···155 foundAt: row.found_at,
156 followed: row.followed || false,
157 dismissed: row.dismissed || false,
158+ followStatus: row.follow_status || {},
159 });
160 }
161 });
+4
src/App.tsx
···11import { useFileUpload } from "./hooks/useFileUpload";
12import { useTheme } from "./hooks/useTheme";
13import Firefly from "./components/Firefly";
014import { DEFAULT_SETTINGS } from "./types/settings";
15import type { UserSettings } from "./types/settings";
16···86 setCurrentStep("loading");
8788 const uploadId = crypto.randomUUID();
008990 searchAllUsers(initialResults, setStatusMessage, () => {
91 setCurrentStep("results");
···302 isFollowing={isFollowing}
303 currentStep={currentStep}
304 sourcePlatform={currentPlatform}
0305 reducedMotion={reducedMotion}
306 isDark={isDark}
307 onToggleTheme={toggleTheme}
···11import { useFileUpload } from "./hooks/useFileUpload";
12import { useTheme } from "./hooks/useTheme";
13import Firefly from "./components/Firefly";
14+import { ATPROTO_APPS } from "./constants/atprotoApps";
15import { DEFAULT_SETTINGS } from "./types/settings";
16import type { UserSettings } from "./types/settings";
17···87 setCurrentStep("loading");
8889 const uploadId = crypto.randomUUID();
90+ const followLexicon =
91+ ATPROTO_APPS[currentDestinationAppId]?.followLexicon;
9293 searchAllUsers(initialResults, setStatusMessage, () => {
94 setCurrentStep("results");
···305 isFollowing={isFollowing}
306 currentStep={currentStep}
307 sourcePlatform={currentPlatform}
308+ destinationAppId={currentDestinationAppId}
309 reducedMotion={reducedMotion}
310 isDark={isDark}
311 onToggleTheme={toggleTheme}