···108108 found_at TIMESTAMP DEFAULT NOW(),
109109 last_verified TIMESTAMP,
110110 is_active BOOLEAN DEFAULT TRUE,
111111+ follow_status JSONB DEFAULT '{}',
112112+ last_follow_check TIMESTAMP,
111113 UNIQUE(source_account_id, atproto_did)
112114 )
113115 `;
···143145 )
144146 `;
145147146146- // ==================== ENHANCED INDEXES FOR PHASE 2 ====================
147147-148148 // Existing indexes
149149 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_to_check ON source_accounts(source_platform, match_found, last_checked)`;
150150 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_platform ON source_accounts(source_platform)`;
···156156 await sql`CREATE INDEX IF NOT EXISTS idx_user_match_status_did_followed ON user_match_status(did, followed)`;
157157 await sql`CREATE INDEX IF NOT EXISTS idx_notification_queue_pending ON notification_queue(sent, created_at) WHERE sent = false`;
158158159159- // NEW: Enhanced indexes for common query patterns
159159+ // ======== Enhanced indexes for common query patterns =========
160160161161 // For sorting
162162 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)`;
···183183184184 // For bulk operations - normalized username lookups
185185 await sql`CREATE INDEX IF NOT EXISTS idx_source_accounts_normalized ON source_accounts(normalized_username, source_platform)`;
186186+187187+ // Follow status indexes
188188+ await sql`CREATE INDEX IF NOT EXISTS idx_atproto_matches_follow_status ON atproto_matches USING gin(follow_status)`;
189189+ await sql`CREATE INDEX IF NOT EXISTS idx_atproto_matches_follow_check ON atproto_matches(last_follow_check)`;
186190187191 console.log("✅ Database indexes created/verified");
188192}
+17-14
netlify/functions/get-upload-details.ts
···8282 // Fetch paginated results with optimized query
8383 const results = await sql`
8484 SELECT
8585- sa.source_username,
8686- sa.normalized_username,
8787- usf.source_date,
8888- am.atproto_did,
8989- am.atproto_handle,
9090- am.atproto_display_name,
9191- am.atproto_avatar,
9292- am.atproto_description,
9393- am.match_score,
9494- am.post_count,
9595- am.follower_count,
9696- am.found_at,
9797- ums.followed,
9898- ums.dismissed,
8585+ sa.source_username,
8686+ sa.normalized_username,
8787+ usf.source_date,
8888+ am.atproto_did,
8989+ am.atproto_handle,
9090+ am.atproto_display_name,
9191+ am.atproto_avatar,
9292+ am.atproto_description,
9393+ am.match_score,
9494+ am.post_count,
9595+ am.follower_count,
9696+ am.found_at,
9797+ am.follow_status,
9898+ am.last_follow_check,
9999+ ums.followed,
100100+ ums.dismissed,
99101 -- Calculate if this is a new match (found after upload creation)
100102 CASE WHEN am.found_at > uu.created_at THEN 1 ELSE 0 END as is_new_match
101103 FROM user_source_follows usf
···153155 foundAt: row.found_at,
154156 followed: row.followed || false,
155157 dismissed: row.dismissed || false,
158158+ followStatus: row.follow_status || {},
156159 });
157160 }
158161 });
+4
src/App.tsx
···1111import { useFileUpload } from "./hooks/useFileUpload";
1212import { useTheme } from "./hooks/useTheme";
1313import Firefly from "./components/Firefly";
1414+import { ATPROTO_APPS } from "./constants/atprotoApps";
1415import { DEFAULT_SETTINGS } from "./types/settings";
1516import type { UserSettings } from "./types/settings";
1617···8687 setCurrentStep("loading");
87888889 const uploadId = crypto.randomUUID();
9090+ const followLexicon =
9191+ ATPROTO_APPS[currentDestinationAppId]?.followLexicon;
89929093 searchAllUsers(initialResults, setStatusMessage, () => {
9194 setCurrentStep("results");
···302305 isFollowing={isFollowing}
303306 currentStep={currentStep}
304307 sourcePlatform={currentPlatform}
308308+ destinationAppId={currentDestinationAppId}
305309 reducedMotion={reducedMotion}
306310 isDark={isDark}
307311 onToggleTheme={toggleTheme}