/** * Hinge WebView Injection Scripts * * JavaScript code injected into Hinge WebView to: * 1. Show consent popup for user approval * 2. Fetch matches and chats from Hinge API * 3. Extract conversation data * 4. Track progress during export * 5. Send data back to React Native * * @reference ChatGPT implementation: src/utils/webviewScripts/chatgpt.ts * * NOTE: API endpoints and token structures are PLACEHOLDERS * These need to be filled in once actual Hinge API endpoints are known */ /** * Consent Popup Script * Shows user consent UI before exporting data */ export declare const HINGE_CONSENT_POPUP_SCRIPT = "\n(function() {\n try {\n const existing = document.getElementById('onairos-consent-overlay');\n if (existing) existing.remove();\n \n const overlay = document.createElement('div');\n overlay.id = 'onairos-consent-overlay';\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.7);\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n `;\n \n const popup = document.createElement('div');\n popup.style.cssText = `\n background: white;\n border-radius: 16px;\n padding: 32px;\n max-width: 400px;\n width: 90%;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n `;\n \n popup.innerHTML = `\n
\n
\uD83D\uDC9C
\n

\n Export Hinge Data?\n

\n

\n Allow Onairos to export your Hinge matches and conversations for analysis?\n

\n
\n \n \n
\n
\n `;\n \n overlay.appendChild(popup);\n document.body.appendChild(overlay);\n \n document.getElementById('onairos-deny').onclick = () => {\n overlay.remove();\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_CONSENT',\n status: 'denied'\n }));\n };\n \n document.getElementById('onairos-allow').onclick = () => {\n document.getElementById('onairos-allow').innerHTML = '\u23F3 Starting...';\n document.getElementById('onairos-allow').disabled = true;\n \n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_CONSENT',\n status: 'allowed'\n }));\n \n setTimeout(() => overlay.remove(), 500);\n };\n } catch (error) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_CONSENT',\n status: 'error',\n message: error.message\n }));\n }\n})();\ntrue;\n"; /** * Export Script - Hinge data extraction * * Hinge API Flow (PLACEHOLDER - needs actual endpoints): * 1. Get authentication token from session/cookies * 2. Fetch matches list from API * 3. Fetch chat history for each match * 4. Extract user messages and match data * * TODO: Fill in actual API endpoints once known: * - HINGE_API_BASE: Base URL for Hinge API * - MATCHES_ENDPOINT: Endpoint to fetch matches * - CHATS_ENDPOINT: Endpoint to fetch chats per match * - AUTH_TOKEN_LOCATION: Where auth token is stored (cookies/localStorage) */ export declare const HINGE_EXPORT_SCRIPT = "\n(function() {\n try {\n console.log('\uD83D\uDD04 [HINGE] Script version: SDK_V1');\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'DEBUG_LOG',\n message: 'Hinge Script version: SDK_V1 loaded'\n }));\n \n if (!document.body) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_ERROR',\n message: 'Page not ready'\n }));\n return;\n }\n\n // ============================================================\n // PLACEHOLDER API CONFIGURATION - REPLACE WITH ACTUAL ENDPOINTS\n // ============================================================\n const HINGE_CONFIG = {\n // Base URL for Hinge API\n baseUrl: 'https://api.hinge.co', // PLACEHOLDER\n \n // API Endpoints - PLACEHOLDERS\n endpoints: {\n // Get user profile/session\n session: '/api/v1/session', // PLACEHOLDER\n // Get matches list\n matches: '/api/v1/matches', // PLACEHOLDER\n // Get chats for a match (append match ID)\n chats: '/api/v1/matches/{matchId}/messages', // PLACEHOLDER\n // Get user preferences/likes\n likes: '/api/v1/likes', // PLACEHOLDER\n },\n \n // How to extract auth token - PLACEHOLDER\n getAuthToken: () => {\n // Try multiple locations where token might be stored\n // Option 1: Cookie\n const cookieMatch = document.cookie.match(/auth_token=([^;]+)/);\n if (cookieMatch) return cookieMatch[1];\n \n // Option 2: localStorage\n const localToken = localStorage.getItem('hinge_auth_token');\n if (localToken) return localToken;\n \n // Option 3: sessionStorage\n const sessionToken = sessionStorage.getItem('hinge_auth_token');\n if (sessionToken) return sessionToken;\n \n // Option 4: Check for token in window object (some apps expose it)\n if (window.__HINGE_TOKEN__) return window.__HINGE_TOKEN__;\n \n return null;\n },\n \n // Request headers template - PLACEHOLDER\n getHeaders: (token) => ({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json',\n 'Authorization': token ? 'Bearer ' + token : undefined,\n 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)',\n // Add any Hinge-specific headers here\n // 'X-Hinge-App-Version': '9.0.0', // PLACEHOLDER\n })\n };\n // ============================================================\n\n console.log('\uD83D\uDE80 [HINGE] Starting extraction...');\n\n (async () => {\n try {\n // STEP 1: Get authentication token\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_PROGRESS',\n stage: 'fetching_token',\n progress: 2,\n message: 'Getting authentication...'\n }));\n\n console.log('[HINGE] Getting auth token...');\n const authToken = HINGE_CONFIG.getAuthToken();\n \n if (!authToken) {\n console.warn('[HINGE] No auth token found - user may not be logged in');\n // Continue anyway - some APIs might work without explicit token\n }\n \n const headers = HINGE_CONFIG.getHeaders(authToken);\n console.log('[HINGE] Auth token:', authToken ? 'Found' : 'Not found');\n\n // STEP 2: Fetch matches list\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_PROGRESS',\n stage: 'fetching_matches',\n progress: 10,\n message: 'Fetching matches...'\n }));\n\n console.log('[HINGE] Fetching matches...');\n \n let matches = [];\n try {\n const matchesResponse = await fetch(HINGE_CONFIG.baseUrl + HINGE_CONFIG.endpoints.matches, {\n method: 'GET',\n credentials: 'include',\n headers: headers\n });\n \n console.log('[HINGE] Matches response:', matchesResponse.status);\n \n if (matchesResponse.ok) {\n const matchesData = await matchesResponse.json();\n // PLACEHOLDER: Adjust based on actual API response structure\n matches = matchesData.matches || matchesData.data || matchesData || [];\n console.log('[HINGE] Found', matches.length, 'matches');\n } else {\n console.warn('[HINGE] Failed to fetch matches:', matchesResponse.status);\n }\n } catch (matchesError) {\n console.warn('[HINGE] Matches fetch error:', matchesError.message);\n }\n\n if (matches.length === 0) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_COMPLETE',\n status: 'success',\n data: {\n matches: [],\n chats: [],\n total_matches: 0,\n total_messages: 0\n },\n success: true,\n message: 'Connected! No matches found.'\n }));\n return;\n }\n\n // STEP 3: Fetch chats for each match (limit to 20 most recent)\n const limitedMatches = matches.slice(0, 20);\n const chatData = [];\n let totalMessages = 0;\n\n for (let i = 0; i < limitedMatches.length; i++) {\n const match = limitedMatches[i];\n // PLACEHOLDER: Adjust match ID field based on actual API\n const matchId = match.id || match.match_id || match.userId;\n const matchName = match.name || match.firstName || match.displayName || 'Match';\n \n const progressPercent = 20 + ((i / limitedMatches.length) * 60);\n\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_PROGRESS',\n stage: 'fetching_chats',\n current: i + 1,\n total: limitedMatches.length,\n progress: Math.round(progressPercent),\n message: 'Fetching chats ' + (i + 1) + '/' + limitedMatches.length\n }));\n\n try {\n if (i > 0) {\n await new Promise(resolve => setTimeout(resolve, 300));\n }\n \n const chatEndpoint = HINGE_CONFIG.endpoints.chats.replace('{matchId}', matchId);\n const chatResponse = await fetch(HINGE_CONFIG.baseUrl + chatEndpoint, {\n method: 'GET',\n credentials: 'include',\n headers: headers\n });\n\n if (chatResponse.ok) {\n const chatRaw = await chatResponse.json();\n // PLACEHOLDER: Adjust based on actual API response structure\n const messages = chatRaw.messages || chatRaw.data || chatRaw || [];\n \n // Extract user messages only\n const userMessages = [];\n for (const msg of messages) {\n // PLACEHOLDER: Adjust sender field based on actual API\n const isUserMessage = msg.is_sender || msg.fromSelf || msg.sender === 'self';\n if (isUserMessage) {\n const content = msg.text || msg.content || msg.body || '';\n if (content.trim()) {\n userMessages.push({\n content: content.trim(),\n timestamp: msg.timestamp || msg.created_at || msg.sent_at\n });\n }\n }\n }\n \n if (userMessages.length > 0) {\n chatData.push({\n match_id: matchId,\n match_name: matchName,\n user_messages: userMessages,\n total_messages: messages.length\n });\n totalMessages += userMessages.length;\n console.log('[HINGE] \u2705 Chat', (i + 1), ':', matchName, '(' + userMessages.length + ' user msgs)');\n }\n } else {\n console.warn('[HINGE] \u26A0\uFE0F Chat fetch failed for', matchId, ':', chatResponse.status);\n }\n } catch (chatError) {\n console.warn('[HINGE] \u26A0\uFE0F Chat error for', matchId, ':', chatError.message);\n }\n }\n\n // STEP 4: Compile and send results\n console.log('\u2705 [HINGE] Extraction complete:', totalMessages, 'messages from', chatData.length, 'chats');\n\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_COMPLETE',\n status: 'success',\n data: {\n matches: limitedMatches.map(m => ({\n id: m.id || m.match_id,\n name: m.name || m.firstName || 'Match',\n // PLACEHOLDER: Add more match fields as needed\n })),\n chats: chatData,\n total_matches: limitedMatches.length,\n total_chats: chatData.length,\n total_messages: totalMessages\n },\n success: true\n }));\n\n } catch (error) {\n console.error('\u274C [HINGE] Export failed:', error.message);\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_ERROR',\n status: 'error',\n message: 'Fetch failed: ' + error.message,\n success: false\n }));\n }\n })();\n\n } catch (error) {\n console.error('\u274C [HINGE] Init error:', error.message);\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'HINGE_EXPORT_ERROR',\n status: 'error',\n message: 'Script error: ' + error.message,\n success: false\n }));\n }\n})();\ntrue;\n"; export declare const HINGE_SUCCESS_SCRIPT = "\n(function() {\n console.log('\u2705 Hinge export successful!');\n})();\ntrue;\n"; export declare const HINGE_ERROR_SCRIPT = "\n(function() {\n console.log('\u274C Hinge export failed!');\n})();\ntrue;\n"; //# sourceMappingURL=hinge.d.ts.map