import { useQuery, useMutation } from '@apollo/client/react'; import { DISCOVER_SEARCH_SETTINGS_QUERY, IS_INCOGNITO_QUERY, POPULAR_LOCATIONS_QUERY, APP_SETTINGS_QUERY, REDEEMED_OFFERS_QUERY, HAS_LINKED_REFLECTION_QUERY } from '../api/operations/queries'; import { DEVICE_LOCATION_UPDATE_MUTATION, SEARCH_SETTINGS_UPDATE_MUTATION, PROFILE_LOCATION_UPDATE_MUTATION, APP_SETTINGS_UPDATE_MUTATION, SYNC_ACCOUNT_MUTATION, ACCOUNT_REDEEM_OFFER_MUTATION, ACCOUNT_DEACTIVATE_MUTATION, ACCOUNT_TERMINATE_MUTATION } from '../api/operations/mutations'; import { saveCustomLocation } from '../api/dataSync'; import { useAuth } from '../hooks/useAuth'; import { authManager } from '../api/auth'; import type { AuthStatus } from '../api/auth'; import { LoadingPage } from '../components/ui/Loading'; import { useLocation, geocodeAddress, BannedCountryError } from '../hooks/useLocation'; import { isCountryBanned, findBannedCountry } from '../config/bannedCountries'; import { useState, useEffect, useCallback } from 'react'; // Inline styles for guaranteed rendering const styles = { container: { maxWidth: '640px', margin: '0 auto', paddingBottom: '32px', }, header: { marginBottom: '32px', }, title: { fontFamily: "'Clash Display', sans-serif", fontSize: '32px', fontWeight: 700, color: '#ffffff', marginBottom: '8px', }, subtitle: { color: '#6b7280', fontSize: '16px', }, card: { background: '#1a1a24', borderRadius: '20px', border: '1px solid rgba(255,255,255,0.08)', marginBottom: '24px', overflow: 'hidden', }, cardContent: { padding: '32px', }, sectionHeader: { display: 'flex', alignItems: 'center', gap: '16px', marginBottom: '28px', }, iconBox: (color: string) => ({ width: '48px', height: '48px', borderRadius: '14px', background: `linear-gradient(135deg, ${color}33 0%, ${color}15 100%)`, display: 'flex', alignItems: 'center', justifyContent: 'center', }), sectionTitle: { fontFamily: "'Clash Display', sans-serif", fontSize: '22px', fontWeight: 600, color: '#ffffff', }, label: { fontSize: '11px', fontWeight: 600, textTransform: 'uppercase' as const, letterSpacing: '0.1em', color: '#6b7280', marginBottom: '12px', display: 'block', }, value: { fontSize: '18px', fontWeight: 500, color: '#ffffff', }, input: { flex: 1, padding: '14px 18px', background: '#24242f', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '12px', color: '#ffffff', fontSize: '15px', outline: 'none', transition: 'border-color 0.2s', }, button: { padding: '14px 24px', background: 'linear-gradient(135deg, #c41e3a 0%, #e91e63 100%)', border: 'none', borderRadius: '12px', color: '#ffffff', fontSize: '15px', fontWeight: 600, cursor: 'pointer', transition: 'opacity 0.2s', }, buttonSecondary: { padding: '10px 18px', background: '#24242f', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '10px', color: '#ffffff', fontSize: '14px', fontWeight: 500, cursor: 'pointer', }, infoBox: { padding: '18px', background: '#24242f', borderRadius: '14px', marginBottom: '20px', }, badge: (variant: 'default' | 'primary' | 'success') => { const colors = { default: { bg: '#24242f', color: '#9ca3af' }, primary: { bg: 'rgba(196, 30, 58, 0.15)', color: '#e91e63' }, success: { bg: 'rgba(34, 197, 94, 0.15)', color: '#22c55e' }, }; const { bg, color } = colors[variant]; return { display: 'inline-block', padding: '8px 14px', background: bg, borderRadius: '8px', color: color, fontSize: '13px', fontWeight: 500, marginRight: '8px', marginBottom: '8px', }; }, toggleContainer: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px', background: '#24242f', borderRadius: '14px', }, toggle: (isOn: boolean) => ({ width: '56px', height: '32px', borderRadius: '16px', background: isOn ? 'linear-gradient(135deg, #c41e3a 0%, #e91e63 100%)' : '#1a1a24', position: 'relative' as const, cursor: 'not-allowed', transition: 'background 0.3s', }), toggleKnob: (isOn: boolean) => ({ position: 'absolute' as const, top: '4px', left: isOn ? '28px' : '4px', width: '24px', height: '24px', borderRadius: '12px', background: '#ffffff', boxShadow: '0 2px 8px rgba(0,0,0,0.3)', transition: 'left 0.3s', }), note: { fontSize: '12px', color: '#6b7280', marginTop: '16px', paddingTop: '16px', borderTop: '1px solid rgba(255,255,255,0.05)', }, savedLocation: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 18px', background: '#24242f', borderRadius: '12px', marginBottom: '10px', cursor: 'pointer', transition: 'background 0.2s', }, setLocationButton: { display: 'flex', alignItems: 'center', gap: '8px', padding: '10px 16px', background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)', border: 'none', borderRadius: '10px', color: '#ffffff', fontSize: '13px', fontWeight: 600, cursor: 'pointer', transition: 'opacity 0.2s, transform 0.2s', boxShadow: '0 4px 12px rgba(59, 130, 246, 0.3)', }, activeLocationBadge: { display: 'inline-flex', alignItems: 'center', gap: '6px', padding: '6px 12px', background: 'rgba(34, 197, 94, 0.15)', borderRadius: '20px', color: '#22c55e', fontSize: '12px', fontWeight: 600, }, }; export function SettingsPage() { const { logout } = useAuth(); const [searchQuery, setSearchQuery] = useState(''); const [searching, setSearching] = useState(false); const [searchError, setSearchError] = useState(null); const [saveLocationName, setSaveLocationName] = useState(''); const [showSaveDialog, setShowSaveDialog] = useState(false); // Desiring For editing state const [editingDesiringFor, setEditingDesiringFor] = useState(false); const [tempDesiringFor, setTempDesiringFor] = useState([]); const [savingDesiringFor, setSavingDesiringFor] = useState(false); // Auth credentials state — backend is single source of truth const [profileIdInput, setProfileIdInput] = useState(authManager.getProfileId()); const [refreshTokenInput, setRefreshTokenInput] = useState(''); const [authStatus, setAuthStatus] = useState(authManager.getStatus()); const [authSaving, setAuthSaving] = useState(false); const [authMessage, setAuthMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); // Location rotation state const [rotationState, setRotationState] = useState(null); const [rotationStatus, setRotationStatus] = useState(null); const [rotationLoading, setRotationLoading] = useState(true); const [rotatingNow, setRotatingNow] = useState(false); const [newGroupName, setNewGroupName] = useState(''); const [newGroupLocationIds, setNewGroupLocationIds] = useState([]); const [showCreateGroup, setShowCreateGroup] = useState(false); const [editingGroupId, setEditingGroupId] = useState(null); const [editGroupLocationIds, setEditGroupLocationIds] = useState([]); // Fetch rotation state const fetchRotationState = useCallback(async () => { try { const [stateRes, statusRes] = await Promise.all([ fetch('/api/location-rotation'), fetch('/api/location-rotation/status'), ]); if (stateRes.ok) setRotationState(await stateRes.json()); if (statusRes.ok) setRotationStatus(await statusRes.json()); } catch (e) { console.error('Failed to fetch rotation state:', e); } finally { setRotationLoading(false); } }, []); useEffect(() => { fetchRotationState(); }, [fetchRotationState]); // Refresh rotation status every 30 seconds useEffect(() => { const interval = setInterval(async () => { try { const res = await fetch('/api/location-rotation/status'); if (res.ok) setRotationStatus(await res.json()); } catch (e) {} }, 30000); return () => clearInterval(interval); }, []); // Load current profile ID from backend on mount useEffect(() => { setProfileIdInput(authManager.getProfileId()); }, []); const [rotationError, setRotationError] = useState(null); // Teleport state const [teleportLoading, setTeleportLoading] = useState(false); const [teleportStatus, setTeleportStatus] = useState(null); const [teleportCity, setTeleportCity] = useState(null); // Notifications/Preferences state const [syncingAccount, setSyncingAccount] = useState(false); const [syncResult, setSyncResult] = useState(null); // Account management state const [offerCode, setOfferCode] = useState(''); const [redeemingOffer, setRedeemingOffer] = useState(false); const [offerResult, setOfferResult] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [pauseConfirm, setPauseConfirm] = useState(false); const [pausingAccount, setPausingAccount] = useState(false); const [deleteConfirm, setDeleteConfirm] = useState(0); // 0=none, 1=first confirm, 2=second confirm const [deletingAccount, setDeletingAccount] = useState(false); const [accountActionResult, setAccountActionResult] = useState<{ type: 'success' | 'error'; text: string } | null>(null); // Teleport queries/mutations const { data: popularLocationsData } = useQuery(POPULAR_LOCATIONS_QUERY); const [profileLocationUpdate] = useMutation(PROFILE_LOCATION_UPDATE_MUTATION); // App settings queries/mutations const { data: appSettingsData, refetch: refetchAppSettings } = useQuery(APP_SETTINGS_QUERY); const [appSettingsUpdate] = useMutation(APP_SETTINGS_UPDATE_MUTATION); const [syncAccount] = useMutation(SYNC_ACCOUNT_MUTATION); // Account queries/mutations const { data: redeemedOffersData, refetch: refetchOffers } = useQuery(REDEEMED_OFFERS_QUERY); const { data: linkedReflectionData } = useQuery(HAS_LINKED_REFLECTION_QUERY); const [redeemOffer] = useMutation(ACCOUNT_REDEEM_OFFER_MUTATION); const [deactivateAccount] = useMutation(ACCOUNT_DEACTIVATE_MUTATION); const [terminateAccount] = useMutation(ACCOUNT_TERMINATE_MUTATION); const handleTeleport = async (loc: any) => { setTeleportLoading(true); setTeleportStatus(null); try { await profileLocationUpdate({ variables: { input: { teleportLocation: { latitude: loc.latitude, longitude: loc.longitude, city: loc.geocode?.city || '', country: loc.geocode?.country || '', }, }, }, }); setTeleportCity(`${loc.geocode?.city || 'Unknown'}, ${loc.geocode?.country || ''}`); setTeleportStatus('success'); } catch (err) { setTeleportStatus('error'); } finally { setTeleportLoading(false); } }; const handleResetTeleport = async () => { setTeleportLoading(true); setTeleportStatus(null); try { await profileLocationUpdate({ variables: { input: { deviceLocation: { latitude: 0, longitude: 0 }, }, }, }); setTeleportCity(null); setTeleportStatus('reset'); } catch (err) { setTeleportStatus('error'); } finally { setTeleportLoading(false); } }; const handleToggleNotification = async (field: string, currentValue: boolean) => { try { await appSettingsUpdate({ variables: { [field]: !currentValue } }); refetchAppSettings(); } catch (err) { console.error('Failed to update setting:', err); } }; const handleSyncAccount = async () => { setSyncingAccount(true); setSyncResult(null); try { const result = await syncAccount(); const data = result.data?.syncAccount; setSyncResult(`Synced: Majestic=${data?.isMajestic ? 'Yes' : 'No'}, Uplift=${data?.isUplift ? 'Yes' : 'No'}, Pings=${data?.availablePings ?? 'N/A'}`); } catch (err) { setSyncResult('Sync failed: ' + (err instanceof Error ? err.message : 'Unknown error')); } finally { setSyncingAccount(false); } }; const handleRedeemOffer = async () => { if (!offerCode.trim()) return; setRedeemingOffer(true); setOfferResult(null); try { await redeemOffer({ variables: { input: { offerName: offerCode.trim() } } }); setOfferResult({ type: 'success', text: `Offer "${offerCode.trim()}" redeemed!` }); setOfferCode(''); refetchOffers(); } catch (err) { setOfferResult({ type: 'error', text: err instanceof Error ? err.message : 'Failed to redeem offer' }); } finally { setRedeemingOffer(false); } }; const handlePauseAccount = async () => { setPausingAccount(true); setAccountActionResult(null); try { await deactivateAccount(); setAccountActionResult({ type: 'success', text: 'Account paused (deactivated).' }); setPauseConfirm(false); } catch (err) { setAccountActionResult({ type: 'error', text: err instanceof Error ? err.message : 'Failed to pause account' }); } finally { setPausingAccount(false); } }; const handleDeleteAccount = async () => { setDeletingAccount(true); setAccountActionResult(null); try { await terminateAccount(); setAccountActionResult({ type: 'success', text: 'Account terminated. You will be logged out.' }); setDeleteConfirm(0); } catch (err) { setAccountActionResult({ type: 'error', text: err instanceof Error ? err.message : 'Failed to delete account' }); } finally { setDeletingAccount(false); } }; const updateRotation = async (updates: Record) => { setRotationError(null); try { // Only send the changed fields — backend merges with file state const res = await fetch('/api/location-rotation', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates), }); if (res.ok) { const data = await res.json(); setRotationState(data.state); // Refresh status const statusRes = await fetch('/api/location-rotation/status'); if (statusRes.ok) setRotationStatus(await statusRes.json()); } } catch (e) { console.error('Failed to update rotation:', e); } }; const handleRotateNow = async () => { setRotatingNow(true); setRotationError(null); try { const res = await fetch('/api/location-rotation/rotate-now', { method: 'POST' }); const data = await res.json(); if (res.ok) { setRotationState(data.state); const statusRes = await fetch('/api/location-rotation/status'); if (statusRes.ok) setRotationStatus(await statusRes.json()); } else { setRotationError(data.error || 'Rotation failed'); } } catch (e) { setRotationError(e instanceof Error ? e.message : 'Rotation failed'); } finally { setRotatingNow(false); } }; const handleCreateGroup = () => { if (!newGroupName.trim() || newGroupLocationIds.length === 0) return; const newGroup = { id: crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(), name: newGroupName.trim(), locationIds: newGroupLocationIds, }; const groups = [...(rotationState?.groups || []), newGroup]; updateRotation({ groups }); setNewGroupName(''); setNewGroupLocationIds([]); setShowCreateGroup(false); }; const handleDeleteGroup = (groupId: string) => { const groups = (rotationState?.groups || []).filter((g: any) => g.id !== groupId); const updates: Record = { groups }; if (rotationState?.activeGroupId === groupId) { updates.activeGroupId = null; updates.enabled = false; } updateRotation(updates); }; const handleSaveEditGroup = (groupId: string) => { const groups = (rotationState?.groups || []).map((g: any) => g.id === groupId ? { ...g, locationIds: editGroupLocationIds } : g ); updateRotation({ groups }); setEditingGroupId(null); setEditGroupLocationIds([]); }; // Subscribe to auth status changes useEffect(() => { const unsubscribe = authManager.subscribe(() => { setAuthStatus(authManager.getStatus()); }); return unsubscribe; }, []); // Refresh auth status every second while on settings page useEffect(() => { const interval = setInterval(() => { setAuthStatus(authManager.getStatus()); }, 1000); return () => clearInterval(interval); }, []); const handleSaveCredentials = async () => { setAuthSaving(true); setAuthMessage(null); try { const profileId = profileIdInput.trim(); const refreshToken = refreshTokenInput.trim(); if (!refreshToken) { setAuthMessage({ type: 'error', text: 'Refresh token is required' }); return; } // Seed directly to backend — single source of truth const resp = await fetch('/api/auth/seed', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken, profileId }), }); if (!resp.ok) { const data = await resp.json().catch(() => ({})); throw new Error(data.error || `HTTP ${resp.status}`); } await authManager.forceRefresh(); setAuthMessage({ type: 'success', text: 'Credentials saved to server! All devices will use the new token.' }); setRefreshTokenInput(''); setTimeout(() => window.location.reload(), 1500); } catch (err) { setAuthMessage({ type: 'error', text: err instanceof Error ? err.message : 'Failed to save credentials' }); } finally { setAuthSaving(false); } }; const handleRefreshToken = async () => { setAuthSaving(true); setAuthMessage(null); try { await authManager.forceRefresh(); setAuthMessage({ type: 'success', text: 'Token refreshed successfully!' }); } catch (err) { setAuthMessage({ type: 'error', text: err instanceof Error ? err.message : 'Failed to refresh token' }); } finally { setAuthSaving(false); } }; const { location, savedLocations, setLocation, saveLocation, deleteLocation, clearLocation, } = useLocation(); const { data: settingsData, loading: settingsLoading } = useQuery( DISCOVER_SEARCH_SETTINGS_QUERY, { variables: { profileId: authManager.getProfileId() }, } ); const { data: incognitoData, loading: incognitoLoading } = useQuery( IS_INCOGNITO_QUERY, { variables: { profileId: authManager.getProfileId() }, } ); const [updateLocation] = useMutation(DEVICE_LOCATION_UPDATE_MUTATION); const [updateSearchSettings] = useMutation(SEARCH_SETTINGS_UPDATE_MUTATION); const [locationStatus, setLocationStatus] = useState<{ type: 'success' | 'error'; text: string } | null>(null); if (settingsLoading || incognitoLoading) return ; const settings = settingsData?.profile; const isIncognito = incognitoData?.profile?.isIncognito; const handleSearch = async () => { if (!searchQuery.trim()) return; setSearching(true); setSearchError(null); try { const result = await geocodeAddress(searchQuery); if (result) { if (isCountryBanned(result.country, result.countryCode)) { const hit = findBannedCountry(result.country, result.countryCode); setSearchError( `Feeld is not available in ${hit?.name ?? result.country ?? 'this country'}. Using a location there will get your account flagged. Pick a different location.` ); return; } const newLocation = { latitude: result.lat, longitude: result.lng, name: result.displayName, country: result.country, countryCode: result.countryCode, }; setLocation(newLocation); await updateLocation({ variables: { input: { latitude: result.lat, longitude: result.lng, }, }, }); setLocationStatus({ type: 'success', text: `Location set to ${result.displayName} — you'll appear to users in this area` }); setTimeout(() => setLocationStatus(null), 4000); setSearchQuery(''); } else { setSearchError('Location not found. Try a different search.'); } } catch (error) { setSearchError('Failed to search location. Please try again.'); } finally { setSearching(false); } }; const handleSelectSavedLocation = async (saved: typeof savedLocations[0]) => { if (isCountryBanned(saved.country, saved.countryCode)) { const hit = findBannedCountry(saved.country, saved.countryCode); setLocationStatus({ type: 'error', text: `Feeld is not available in ${hit?.name ?? saved.country ?? 'that country'}. Remove this saved location and pick a different one.`, }); setTimeout(() => setLocationStatus(null), 5000); return; } const newLocation = { latitude: saved.latitude, longitude: saved.longitude, name: saved.name, country: saved.country, countryCode: saved.countryCode, }; setLocation(newLocation); setLocationStatus(null); try { await updateLocation({ variables: { input: { latitude: saved.latitude, longitude: saved.longitude, }, }, }); // API returns 0,0 as privacy masking — location IS set server-side setLocationStatus({ type: 'success', text: `Location set to ${saved.name} — you'll appear to users in this area` }); setTimeout(() => setLocationStatus(null), 4000); } catch (error) { console.error('Failed to update location:', error); setLocationStatus({ type: 'error', text: 'Failed to update location' }); setTimeout(() => setLocationStatus(null), 4000); } }; const handleSaveCurrentLocation = () => { if (!location || !saveLocationName.trim()) return; try { saveLocation(saveLocationName.trim(), location.latitude, location.longitude, { country: location.country, countryCode: location.countryCode, }); setSaveLocationName(''); setShowSaveDialog(false); } catch (err) { if (err instanceof BannedCountryError) { setLocationStatus({ type: 'error', text: `Can't save — Feeld is not available in ${err.bannedCountry}.`, }); setTimeout(() => setLocationStatus(null), 5000); } else { throw err; } } }; // Desiring For options (from API LocalisedDesireCategory) const DESIRE_OPTIONS = [ 'AFTERCARE', 'BDSM', 'BONDAGE', 'BRAT', 'BRAT_TAMER', 'CASUAL', 'CELIBATE', 'COMMUNICATION', 'CONNECTION', 'COUPLES', 'CUDDLING', 'DATES', 'DOMINANTS', 'EDGING', 'ENM', 'EXPLORATION', 'FF', 'FFF', 'FFFF', 'FFM', 'FLIRTING', 'FOREPLAY', 'FREEDOMME', 'FRIENDSHIPS', 'FUN', 'FWB', 'GGG', 'GROUP', 'INTIMACY', 'KINK', 'KISSING', 'MASSAGE', 'MF', 'MFMF', 'MM', 'MMF', 'MMM', 'MMMM', 'MONOGAMY', 'OPEN_RELATIONSHIP', 'PARTIES', 'POLY', 'RELATIONSHIP', 'ROLE_PLAY', 'ROUGH', 'SENSUAL', 'SINGLES', 'SUBMISSIVES', 'SWITCH', 'TEXTING', 'THREESOME', 'TOYS', 'VANILLA', 'WATCHING' ]; const handleStartEditDesiringFor = () => { setTempDesiringFor(settings?.desiringFor || []); setEditingDesiringFor(true); }; const handleCancelEditDesiringFor = () => { setEditingDesiringFor(false); setTempDesiringFor([]); }; const handleToggleDesiringFor = (option: string) => { setTempDesiringFor(prev => prev.includes(option) ? prev.filter(o => o !== option) : [...prev, option] ); }; const handleSaveDesiringFor = async () => { setSavingDesiringFor(true); try { await updateSearchSettings({ variables: { desiringFor: tempDesiringFor, }, }); setEditingDesiringFor(false); // Refetch settings to update the display window.location.reload(); } catch (error) { console.error('Failed to update desiring for:', error); } finally { setSavingDesiringFor(false); } }; return (
{/* Header */}

Settings

Manage your preferences

{/* Location Settings */}

Location

{/* Current Location */}
{location ? (

{location.name || 'Custom Location'}

{location.latitude.toFixed(4)}, {location.longitude.toFixed(4)}

{!showSaveDialog && ( )}
) : (

Using default location

)}
{/* Save Location Dialog */} {showSaveDialog && location && (
setSaveLocationName(e.target.value)} placeholder="e.g., Work, Home, NYC" style={styles.input} />
)} {/* Search Location */}
setSearchQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} placeholder="Enter city, address, or place..." style={styles.input} />
{searchError && (

{searchError}

)}
{/* Saved Locations */} {savedLocations.length > 0 && (
{savedLocations.map((saved) => { const isActive = location && Math.abs(location.latitude - saved.latitude) < 0.0001 && Math.abs(location.longitude - saved.longitude) < 0.0001; return (
e.currentTarget.style.background = '#2e2e3a'} onMouseLeave={(e) => e.currentTarget.style.background = '#24242f'} >

{saved.name}

{isActive && ( Active )}

{saved.latitude.toFixed(4)}, {saved.longitude.toFixed(4)}

{!isActive && ( )}
); })}
)} {/* Location status toast */} {locationStatus && (
{locationStatus.text}
)}
{/* Location Rotation */}

Location Rotation

{rotationLoading ? (

Loading rotation settings...

) : ( <> {/* Enable/Disable Toggle */}
updateRotation({ enabled: !rotationState?.enabled })}>

Auto-Rotate

Automatically cycle between location groups

{/* Interval Selector */}
{[2, 4, 6, 8].map(h => ( ))}
{/* Location Groups */}
{/* Create Group Form */} {showCreateGroup && (
setNewGroupName(e.target.value)} placeholder="e.g., DFW, Austin Area" style={styles.input} />
{savedLocations.map(loc => { const isSelected = newGroupLocationIds.includes(loc.id); return ( ); })}
{savedLocations.length === 0 && (

No saved locations. Add locations above first.

)}
)} {/* Existing Groups */} {(rotationState?.groups || []).map((group: any) => { const isActive = rotationState?.activeGroupId === group.id; const isEditing = editingGroupId === group.id; const groupLocations = savedLocations.filter(l => group.locationIds.includes(l.id)); return (

{group.name}

{isActive && ( Active )}
{!isActive ? ( ) : ( )}
{/* Location list / edit mode */} {isEditing ? (
{savedLocations.map(loc => { const isSelected = editGroupLocationIds.includes(loc.id); return ( ); })}
) : (
{groupLocations.map(loc => ( {loc.name} ))} {groupLocations.length === 0 && ( No locations (may need to re-save locations) )}
)}
); })} {(rotationState?.groups || []).length === 0 && !showCreateGroup && (

No groups yet. Create one to start rotating.

)}
{/* Status Display */} {rotationState?.enabled && rotationStatus && (
{rotationStatus.currentLocation && (

Current Location

{rotationStatus.currentLocation.name}

)} {rotationStatus.nextRotation && (

Next Rotation

{new Date(rotationStatus.nextRotation).toLocaleString()}

)} {rotationStatus.lastResult && (

Last Result

{rotationStatus.lastResult.status === 'success' ? `${rotationStatus.lastResult.profilesFound} profiles at ${rotationStatus.lastResult.location}` : `Error: ${rotationStatus.lastResult.error}` }

{rotationStatus.lastResult.timestamp && (

{new Date(rotationStatus.lastResult.timestamp).toLocaleString()}

)}
)}
)} {/* Error display */} {rotationError && (
{rotationError}
)}

Rotates your location between grouped cities on a schedule. Updates your Feeld location and runs a discovery query to appear active. Works even when the browser is closed (runs on the backend).

)}
{/* Discovery Settings */}

Discovery

{settings?.ageRange?.[0] || 18} - {settings?.ageRange?.[1] || 99}

{settings?.distanceMax || 100} miles

{settings?.lookingFor?.map((item: string) => ( {item.replace(/_/g, ' ')} ))}
{!editingDesiringFor && ( )}
{editingDesiringFor ? (

Select what you're looking for. Profiles matching these desires will appear in discovery.

{DESIRE_OPTIONS.map((option) => { const isSelected = tempDesiringFor.includes(option); return ( ); })}
) : (
{settings?.desiringFor && settings.desiringFor.length > 0 ? ( settings.desiringFor.map((item: string) => ( {item.replace(/_/g, ' ')} )) ) : ( No filters set )}
)}

Age range, max distance, and looking for are read-only. Desiring For can be edited above.

{/* Privacy Settings */}

Privacy

Incognito Mode

Browse without being seen

Privacy toggle is read-only in this version

{/* Teleport */}

Teleport

{/* Teleport Status Badge */}
{teleportCity ? ( Teleporting to {teleportCity} ) : ( Device Location )} {teleportStatus === 'error' && (

Failed to update teleport location.

)}
{/* Popular Locations Grid */}
{(popularLocationsData?.popularLocations || []).map((loc: any, i: number) => ( ))}
{teleportLoading && (

Teleporting...

)}
{/* Reset to Device Location */}

Teleport your profile to appear in a different city. Popular locations are provided by Feeld. Reset to return to your device location.

{/* Notifications & Preferences */}

Notifications & Preferences

{(() => { const acct = appSettingsData?.account; const appSettings = acct?.appSettings; if (!appSettings) return

Loading notification settings...

; const toggles: Array<{ label: string; field: string; value: boolean }> = [ { label: 'New Connection', field: 'receiveNewConnectionPushNotifications', value: !!appSettings.receiveNewConnectionPushNotifications }, { label: 'New Ping', field: 'receiveNewPingPushNotifications', value: !!appSettings.receiveNewPingPushNotifications }, { label: 'New Message', field: 'receiveNewMessagePushNotifications', value: !!appSettings.receiveNewMessagePushNotifications }, { label: 'New Like', field: 'receiveNewLikePushNotifications', value: !!appSettings.receiveNewLikePushNotifications }, { label: 'Marketing', field: 'receiveMarketingNotifications', value: !!appSettings.receiveMarketingNotifications }, { label: 'News Email', field: 'receiveNewsEmailNotifications', value: !!appSettings.receiveNewsEmailNotifications }, { label: 'Promotions Email', field: 'receivePromotionsEmailNotifications', value: !!appSettings.receivePromotionsEmailNotifications }, { label: 'News Push', field: 'receiveNewsPushNotifications', value: !!appSettings.receiveNewsPushNotifications }, { label: 'Promotions Push', field: 'receivePromotionsPushNotifications', value: !!appSettings.receivePromotionsPushNotifications }, ]; return ( <>
{toggles.map(({ label, field, value }) => (
handleToggleNotification(field, value)} >

{label}

))}
{/* Distance Units */}
handleToggleNotification('isDistanceInMiles', !!acct?.isDistanceInMiles)} >

Distance in Miles

{acct?.isDistanceInMiles ? 'Using miles' : 'Using kilometers'}

{/* Sync Account */} {syncResult && (

{syncResult}

)} ); })()}

Toggle notification preferences for your account. Changes are saved immediately. Sync Account refreshes your subscription status from the server.

{/* Account Management */}

Account

{/* Subscription Status */}
{appSettingsData?.account?.isMajestic ? 'Majestic' : 'Free'} {appSettingsData?.account?.isUplift && ( Uplift Active )} {appSettingsData?.account?.availablePings ?? '?'} Pings Available
{/* Reflect Link Status */}
{linkedReflectionData?.hasLinkedReflection ? 'Linked' : 'Not Linked'}
{/* Redeemed Offers */}
{(redeemedOffersData?.account?.redeemedOffers || []).length > 0 ? (
{(redeemedOffersData?.account?.redeemedOffers || []).map((offer: any, i: number) => (

{offer.offerName}

{offer.redeemedAt && (

Redeemed: {new Date(offer.redeemedAt).toLocaleDateString()}

)}
))}
) : (

No offers redeemed

)}
{/* Redeem Offer */}
setOfferCode(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleRedeemOffer()} placeholder="Enter offer code..." style={styles.input} />
{offerResult && (

{offerResult.text}

)}
{/* Pause Account */}
{!pauseConfirm ? ( ) : (

Are you sure you want to pause your account? Your profile will be hidden.

)}
{/* Delete Account */}
{deleteConfirm === 0 && ( )} {deleteConfirm === 1 && (

This will permanently delete your account. This action cannot be undone. Are you sure?

)} {deleteConfirm === 2 && (

FINAL WARNING: This is irreversible. Your account, matches, and messages will be permanently deleted.

)}
{/* Account Action Result */} {accountActionResult && (
{accountActionResult.text}
)}

Pausing hides your profile temporarily. Deleting permanently removes your account, all data, matches, and messages.

{/* Auth Credentials */}

API Credentials

{/* Auth Status */}
{authStatus.isAuthenticated ? 'Valid' : 'Expired / Invalid'}
{/* Current Profile ID */}

{authManager.getProfileId()}

{/* Refresh Token (managed by backend) */}

Managed by backend server

{/* Access Token */} {authStatus.accessToken && (

{authStatus.accessToken}

)} {/* Expiry */} {authStatus.expiresIn && (

Expires in: {Math.floor(authStatus.expiresIn / 60)}m {authStatus.expiresIn % 60}s

)} {authStatus.lastError && (

Error: {authStatus.lastError}

)}
{/* Profile ID */}
setProfileIdInput(e.target.value)} placeholder="profile#xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" style={{ ...styles.input, width: '100%', fontFamily: 'monospace', fontSize: '13px' }} />
{/* Refresh Token */}