UNPKG

@oxyhq/services

Version:

OxyHQ Expo/React Native SDK — UI components, screens, and native features

758 lines (744 loc) 29.5 kB
"use strict"; import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react'; import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator, ScrollView, Alert, Platform, Image } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Avatar from "../components/Avatar.js"; import OxyIcon from "../components/icon/OxyIcon.js"; import { fontFamilies } from "../styles/fonts.js"; import { toast } from '../../lib/sonner'; import { confirmAction } from "../utils/confirmAction.js"; import { Section, GroupedSection, GroupedItem } from "../components/index.js"; import { useI18n } from "../hooks/useI18n.js"; import { useThemeStyles } from "../hooks/useThemeStyles.js"; import { getDisplayName, getShortDisplayName } from "../utils/userUtils.js"; import { useColorScheme } from "../hooks/useColorScheme.js"; import { normalizeTheme } from "../utils/themeUtils.js"; import { useOxy } from "../context/OxyContext.js"; import { useUsersBySessions } from "../hooks/queries/useAccountQueries.js"; import { SECTION_GAP, HEADER_PADDING_TOP_OVERVIEW, createScreenContentStyle } from "../constants/spacing.js"; import { DeleteAccountModal } from "../components/modals/index.js"; // Optional Lottie import - gracefully handle if not available import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; let LottieView = null; try { // eslint-disable-next-line @typescript-eslint/no-require-imports LottieView = require('lottie-react-native').default; } catch { // Lottie not available, will use fallback } // Import Lottie animation - will be undefined if file doesn't exist let lottieAnimation = undefined; try { // eslint-disable-next-line @typescript-eslint/no-require-imports lottieAnimation = require('../assets/lottie/welcomeheader_background_op1.json'); } catch { // Animation file not available } /** * AccountOverviewScreen - Optimized for performance * * Performance optimizations implemented: * - useMemo for theme calculations (only recalculates when theme changes) * - useMemo for additional accounts filtering (only recalculates when dependencies change) * - useCallback for event handlers to prevent unnecessary re-renders * - React.memo wrapper to prevent re-renders when props haven't changed * - GroupedSection components for better organization and cleaner code */ const AccountOverviewScreen = ({ onClose, theme, navigate }) => { // Use useOxy() hook for OxyContext values const { user, logout, isLoading, sessions, activeSessionId, oxyServices, isAuthenticated, openAvatarPicker } = useOxy(); const { t } = useI18n(); const [showMoreAccounts, setShowMoreAccounts] = useState(false); const [additionalAccountsData, setAdditionalAccountsData] = useState([]); const [loadingAdditionalAccounts, setLoadingAdditionalAccounts] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const lottieRef = useRef(null); const hasPlayedRef = useRef(false); const insets = useSafeAreaInsets(); // Use centralized theme styles hook for consistency const colorScheme = useColorScheme(); const normalizedTheme = normalizeTheme(theme); const baseThemeStyles = useThemeStyles(normalizedTheme, colorScheme); const themeStyles = useMemo(() => ({ ...baseThemeStyles, // AccountOverviewScreen uses a custom primary color (purple) instead of the default blue primaryColor: '#d169e5', // Keep custom icon color for this screen iconColor: baseThemeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }), [baseThemeStyles]); // Compute user data for display const displayName = useMemo(() => getDisplayName(user), [user]); const shortDisplayName = useMemo(() => getShortDisplayName(user), [user]); const avatarUrl = useMemo(() => { if (user?.avatar && oxyServices) { return oxyServices.getFileDownloadUrl(user.avatar, 'thumb'); } return undefined; }, [user?.avatar, oxyServices]); // Handle avatar press - use openAvatarPicker from context const handleAvatarPress = useCallback(() => { openAvatarPicker(); }, [openAvatarPicker]); // Play Lottie animation once when component mounts useEffect(() => { if (hasPlayedRef.current || !LottieView || !lottieRef.current || !lottieAnimation) return; const timer = setTimeout(() => { if (lottieRef.current && !hasPlayedRef.current) { lottieRef.current.play(); hasPlayedRef.current = true; } }, 100); return () => clearTimeout(timer); }, []); // Memoize additional accounts filtering to prevent recalculation on every render const additionalAccounts = useMemo(() => (sessions || []).filter(session => session.sessionId !== activeSessionId && session.userId !== user?.id), [sessions, activeSessionId, user?.id]); // Load user profiles for additional accounts using TanStack Query const sessionIds = additionalAccounts.map(s => s.sessionId); const { data: usersData, isLoading: isLoadingUsers } = useUsersBySessions(sessionIds, { enabled: additionalAccounts.length > 0 }); React.useEffect(() => { if (usersData && usersData.length > 0) { const accountsData = usersData.map(({ sessionId, user: userProfile }) => { if (!userProfile) { return { id: sessionId, sessionId, username: 'Unknown User', email: 'No email available', avatar: null, userProfile: null }; } return { id: sessionId, sessionId, username: userProfile.username, email: userProfile.email, name: userProfile.name, avatar: userProfile.avatar, userProfile }; }); setAdditionalAccountsData(accountsData); setLoadingAdditionalAccounts(false); } else if (additionalAccounts.length === 0) { setAdditionalAccountsData([]); setLoadingAdditionalAccounts(false); } else if (!isLoadingUsers) { setLoadingAdditionalAccounts(false); } }, [usersData, additionalAccounts.length, isLoadingUsers]); // Feature settings (with mock values) const features = { safeSearch: false, language: 'English' }; // Memoize event handlers to prevent recreation on every render const handleLogout = useCallback(async () => { try { await logout(); if (onClose) { onClose(); } } catch (error) { if (__DEV__) { console.error('Logout failed:', error); } toast.error(t('common.errors.signOutFailed')); } }, [logout, onClose]); const confirmLogout = useCallback(() => { confirmAction(t('common.confirms.signOut'), handleLogout); }, [handleLogout]); const handleAddAccount = useCallback(() => { toast.info(t('accountOverview.addAccountComing')); }, [t]); const handleSignOutAll = useCallback(() => { confirmAction(t('common.confirms.signOutAll'), handleLogout); }, [handleLogout]); const handleDownloadData = useCallback(async () => { if (!oxyServices || !user) { toast.error(t('accountOverview.items.downloadData.error') || 'Service not available'); return; } try { Alert.alert(t('accountOverview.items.downloadData.confirmTitle') || 'Download Account Data', t('accountOverview.items.downloadData.confirmMessage') || 'Choose the format for your account data export:', [{ text: t('common.cancel') || 'Cancel', style: 'cancel' }, { text: 'JSON', onPress: async () => { try { toast.loading(t('accountOverview.items.downloadData.downloading') || 'Preparing download...'); const blob = await oxyServices.downloadAccountData('json'); // Create download link for web if (Platform.OS === 'web') { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `account-data-${Date.now()}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); toast.success(t('accountOverview.items.downloadData.success') || 'Data downloaded successfully'); } else { // For React Native, you'd need to use a library like expo-file-system toast.success(t('accountOverview.items.downloadData.success') || 'Data downloaded successfully'); } } catch (error) { if (__DEV__) { console.error('Failed to download data:', error); } toast.error(error?.message || t('accountOverview.items.downloadData.error') || 'Failed to download data'); } } }, { text: 'CSV', onPress: async () => { try { toast.loading(t('accountOverview.items.downloadData.downloading') || 'Preparing download...'); const blob = await oxyServices.downloadAccountData('csv'); // Create download link for web if (Platform.OS === 'web') { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `account-data-${Date.now()}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); toast.success(t('accountOverview.items.downloadData.success') || 'Data downloaded successfully'); } else { // For React Native, you'd need to use a library like expo-file-system toast.success(t('accountOverview.items.downloadData.success') || 'Data downloaded successfully'); } } catch (error) { if (__DEV__) { console.error('Failed to download data:', error); } toast.error(error?.message || t('accountOverview.items.downloadData.error') || 'Failed to download data'); } } }]); } catch (error) { if (__DEV__) { console.error('Failed to initiate download:', error); } toast.error(error?.message || t('accountOverview.items.downloadData.error') || 'Failed to download data'); } }, [oxyServices, user, t]); const handleDeleteAccount = useCallback(() => { if (!user) { toast.error(t('accountOverview.items.deleteAccount.error') || 'User not available'); return; } setShowDeleteModal(true); }, [user, t]); const handleConfirmDelete = useCallback(async password => { if (!oxyServices || !user) { throw new Error(t('accountOverview.items.deleteAccount.error') || 'Service not available'); } await oxyServices.deleteAccount(password); toast.success(t('accountOverview.items.deleteAccount.success') || 'Account deleted successfully'); setShowDeleteModal(false); await logout(); if (onClose) { onClose(); } }, [oxyServices, user, logout, onClose, t]); if (!isAuthenticated) { return /*#__PURE__*/_jsx(View, { style: [styles.container, { backgroundColor: themeStyles.backgroundColor }], children: /*#__PURE__*/_jsx(Text, { style: [styles.message, { color: themeStyles.textColor }], children: t('common.status.notSignedIn') }) }); } if (isLoading) { return /*#__PURE__*/_jsx(View, { style: [styles.container, { backgroundColor: themeStyles.backgroundColor, justifyContent: 'center' }], children: /*#__PURE__*/_jsx(ActivityIndicator, { size: "large", color: themeStyles.primaryColor }) }); } return /*#__PURE__*/_jsxs(View, { style: [styles.container, { backgroundColor: themeStyles.backgroundColor }], children: [/*#__PURE__*/_jsxs(ScrollView, { style: styles.content, contentContainerStyle: styles.scrollContent, showsVerticalScrollIndicator: false, children: [user && /*#__PURE__*/_jsx(View, { style: styles.headerSection, children: /*#__PURE__*/_jsxs(View, { style: styles.avatarSectionWrapper, children: [/*#__PURE__*/_jsxs(View, { style: styles.avatarContainer, children: [LottieView && lottieAnimation && /*#__PURE__*/_jsx(LottieView, { ref: lottieRef, source: lottieAnimation, style: styles.lottieBackground, loop: false, autoPlay: false }), /*#__PURE__*/_jsx(TouchableOpacity, { style: styles.avatarWrapper, onPress: handleAvatarPress, activeOpacity: 0.8, children: /*#__PURE__*/_jsx(Avatar, { uri: avatarUrl, name: displayName, size: 100, theme: normalizedTheme }) })] }), /*#__PURE__*/_jsxs(View, { style: styles.nameWrapper, children: [/*#__PURE__*/_jsx(Text, { style: [styles.welcomeText, { color: themeStyles.textColor }], children: displayName }), /*#__PURE__*/_jsx(Text, { style: [styles.welcomeSubtext, { color: themeStyles.isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Manage your Oxy account." })] })] }) }), /*#__PURE__*/_jsx(Section, { title: t('accountOverview.sections.profile'), isFirst: true, children: /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'profile-info', icon: 'account', iconColor: baseThemeStyles.colors.iconSecurity, title: displayName, subtitle: user ? user.email || user.username : t('common.status.loading') || 'Loading...', onPress: () => navigate?.('AccountSettings', { activeTab: 'profile' }) }] }) }), /*#__PURE__*/_jsx(Section, { title: t('accountOverview.sections.accountSettings'), children: /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'edit-profile', icon: 'account-circle', iconColor: baseThemeStyles.colors.iconPersonalInfo, title: t('accountOverview.items.editProfile.title'), subtitle: t('accountOverview.items.editProfile.subtitle'), onPress: () => navigate?.('AccountSettings', { activeTab: 'profile' }) }, { id: 'security-privacy', icon: 'shield-check', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.items.security.title'), subtitle: t('accountOverview.items.security.subtitle'), onPress: () => navigate?.('AccountSettings', { activeTab: 'password' }) }, { id: 'notifications', icon: 'bell', iconColor: baseThemeStyles.colors.iconStorage, title: t('accountOverview.items.notifications.title'), subtitle: t('accountOverview.items.notifications.subtitle'), onPress: () => navigate?.('AccountSettings', { activeTab: 'notifications' }) }, { id: 'premium-subscription', icon: 'star', iconColor: baseThemeStyles.colors.iconPayments, title: t('accountOverview.items.premium.title'), subtitle: user?.isPremium ? t('accountOverview.items.premium.manage') : t('accountOverview.items.premium.upgrade'), onPress: () => navigate?.('PremiumSubscription') }, ...(user?.isPremium ? [{ id: 'billing-management', icon: 'card', iconColor: baseThemeStyles.colors.iconPersonalInfo, title: t('accountOverview.items.billing.title'), subtitle: t('accountOverview.items.billing.subtitle'), onPress: () => toast.info(t('accountOverview.items.billing.coming')) }] : [])] }) }), showMoreAccounts && /*#__PURE__*/_jsx(Section, { title: `${t('accountOverview.sections.additionalAccounts') || 'Additional Accounts'}${additionalAccountsData.length > 0 ? ` (${additionalAccountsData.length})` : ''}`, children: loadingAdditionalAccounts ? /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'loading-accounts', icon: 'sync', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.loadingAdditional.title') || 'Loading accounts...', subtitle: t('accountOverview.loadingAdditional.subtitle') || 'Please wait while we load your additional accounts', customContent: /*#__PURE__*/_jsxs(View, { style: styles.loadingContainer, children: [/*#__PURE__*/_jsx(ActivityIndicator, { size: "small", color: baseThemeStyles.colors.iconSecurity }), /*#__PURE__*/_jsx(Text, { style: styles.loadingText, children: t('accountOverview.loadingAdditional.title') || 'Loading accounts...' })] }) }] }) : additionalAccountsData.length > 0 ? /*#__PURE__*/_jsx(GroupedSection, { items: additionalAccountsData.map((account, index) => ({ id: `account-${account.id}`, icon: 'account', iconColor: baseThemeStyles.colors.iconData, title: typeof account.name === 'object' ? account.name?.full || account.name?.first || account.username : account.name || account.username, subtitle: account.email || account.username, onPress: () => { toast.info(t('accountOverview.items.accountSwitcher.switchPrompt', { username: account.username }) || `Switch to ${account.username}?`); }, customContent: /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsx(View, { style: styles.userIcon, children: account.avatar ? /*#__PURE__*/_jsx(Image, { source: { uri: oxyServices.getFileDownloadUrl(account.avatar, 'thumb') }, style: styles.accountAvatarImage }) : /*#__PURE__*/_jsx(View, { style: styles.accountAvatarFallback, children: /*#__PURE__*/_jsx(Text, { style: styles.accountAvatarText, children: account.username?.charAt(0).toUpperCase() || '?' }) }) }), /*#__PURE__*/_jsx(OxyIcon, { name: "chevron-forward", size: 16, color: "#ccc" })] }) })) }) : /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'no-accounts', icon: 'account-outline', iconColor: '#ccc', title: t('accountOverview.additional.noAccounts.title') || 'No other accounts', subtitle: t('accountOverview.additional.noAccounts.subtitle') || 'Add another account to switch between them' }] }) }), showMoreAccounts && /*#__PURE__*/_jsx(Section, { title: t('accountOverview.sections.accountManagement') || 'Account Management', children: /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'add-account', icon: 'add', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.items.addAccount.title') || 'Add Another Account', subtitle: t('accountOverview.items.addAccount.subtitle') || 'Sign in with a different account', onPress: handleAddAccount }, { id: 'sign-out-all', icon: 'logout', iconColor: baseThemeStyles.colors.iconSharing, title: t('accountOverview.items.signOutAll.title') || 'Sign out of all accounts', subtitle: t('accountOverview.items.signOutAll.subtitle') || 'Remove all accounts from this device', onPress: handleSignOutAll }] }) }), /*#__PURE__*/_jsx(Section, { title: t('accountOverview.sections.quickActions'), children: /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'account-switcher', icon: 'account-group', iconColor: baseThemeStyles.colors.iconData, title: showMoreAccounts ? t('accountOverview.items.accountSwitcher.titleHide') : t('accountOverview.items.accountSwitcher.titleShow'), subtitle: showMoreAccounts ? t('accountOverview.items.accountSwitcher.subtitleHide') : additionalAccountsData.length > 0 ? t('accountOverview.items.accountSwitcher.subtitleSwitchBetween', { count: String(additionalAccountsData.length + 1) }) : loadingAdditionalAccounts ? t('accountOverview.items.accountSwitcher.subtitleLoading') : t('accountOverview.items.accountSwitcher.subtitleManageMultiple'), onPress: () => setShowMoreAccounts(!showMoreAccounts) }, { id: 'history-view', icon: 'clock', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.items.history.title') || 'History', subtitle: t('accountOverview.items.history.subtitle') || 'View and manage your search history', onPress: () => navigate?.('HistoryView') }, { id: 'saves-collections', icon: 'bookmark', iconColor: baseThemeStyles.colors.iconStorage, title: t('accountOverview.items.saves.title') || 'Saves & Collections', subtitle: t('accountOverview.items.saves.subtitle') || 'View your saved items and collections', onPress: () => navigate?.('SavesCollections') }, { id: 'download-data', icon: 'download', iconColor: baseThemeStyles.colors.iconPersonalInfo, title: t('accountOverview.items.downloadData.title'), subtitle: t('accountOverview.items.downloadData.subtitle'), onPress: handleDownloadData }, { id: 'delete-account', icon: 'delete', iconColor: baseThemeStyles.colors.iconSharing, title: t('accountOverview.items.deleteAccount.title'), subtitle: t('accountOverview.items.deleteAccount.subtitle'), onPress: handleDeleteAccount }] }) }), /*#__PURE__*/_jsx(Section, { title: t('accountOverview.sections.support'), children: /*#__PURE__*/_jsx(GroupedSection, { items: [{ id: 'search-settings', icon: 'magnify', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.items.searchSettings.title') || 'Search Settings', subtitle: t('accountOverview.items.searchSettings.subtitle') || 'SafeSearch and personalization', onPress: () => navigate?.('SearchSettings') }, { id: 'language-settings', icon: 'translate', iconColor: baseThemeStyles.colors.iconPersonalInfo, title: t('accountOverview.items.language.title') || 'Language', subtitle: t('accountOverview.items.language.subtitle') || 'Choose your preferred language', onPress: () => navigate?.('LanguageSelector') }, { id: 'account-preferences', icon: 'cog', iconColor: '#8E8E93', title: t('accountOverview.items.preferences.title'), subtitle: t('accountOverview.items.preferences.subtitle'), onPress: () => toast.info(t('accountOverview.items.preferences.coming')) }, { id: 'help-support', icon: 'help-circle', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.items.help.title'), subtitle: t('accountOverview.items.help.subtitle'), onPress: () => navigate?.('HelpSupport') }, { id: 'privacy-policy', icon: 'shield-check', iconColor: baseThemeStyles.colors.iconPersonalInfo, title: t('accountOverview.items.privacyPolicy.title') || 'Privacy Policy', subtitle: t('accountOverview.items.privacyPolicy.subtitle') || 'How we handle your data', onPress: () => navigate?.('LegalDocuments', { initialStep: 1 }) }, { id: 'terms-of-service', icon: 'file-document', iconColor: baseThemeStyles.colors.iconSecurity, title: t('accountOverview.items.termsOfService.title') || 'Terms of Service', subtitle: t('accountOverview.items.termsOfService.subtitle') || 'Terms and conditions of use', onPress: () => navigate?.('LegalDocuments', { initialStep: 2 }) }, { id: 'connected-apps', icon: 'link', iconColor: baseThemeStyles.colors.iconPersonalInfo, title: t('accountOverview.items.connectedApps.title'), subtitle: t('accountOverview.items.connectedApps.subtitle'), onPress: () => toast.info(t('accountOverview.items.connectedApps.coming')) }, { id: 'about', icon: 'information', iconColor: '#8E8E93', title: t('accountOverview.items.about.title'), subtitle: t('accountOverview.items.about.subtitle'), onPress: () => navigate?.('AppInfo') }] }) }), /*#__PURE__*/_jsx(Section, { title: t('accountOverview.sections.actions'), children: /*#__PURE__*/_jsx(GroupedItem, { icon: "logout", iconColor: "#FF3B30", title: t('accountOverview.items.signOut.title'), subtitle: t('accountOverview.items.signOut.subtitle'), onPress: confirmLogout, isFirst: true, isLast: true, showChevron: false }) })] }), user && /*#__PURE__*/_jsx(DeleteAccountModal, { visible: showDeleteModal, username: user.username || '', onClose: () => setShowDeleteModal(false), onDelete: handleConfirmDelete, colors: { background: themeStyles.backgroundColor, text: themeStyles.textColor, secondaryText: themeStyles.isDarkTheme ? '#888888' : '#666666', border: themeStyles.borderColor, danger: '#FF3B30', inputBackground: themeStyles.isDarkTheme ? '#333333' : '#F5F5F5' }, t: t })] }); }; const styles = StyleSheet.create({ container: { flex: 1 }, content: { flex: 1 }, scrollContent: createScreenContentStyle(HEADER_PADDING_TOP_OVERVIEW), headerSection: { alignItems: 'center', marginBottom: SECTION_GAP, paddingTop: HEADER_PADDING_TOP_OVERVIEW }, avatarSectionWrapper: { alignItems: 'center', justifyContent: 'center', width: '100%' }, avatarContainer: { position: 'relative', alignItems: 'center', justifyContent: 'center', width: '100%', maxWidth: 600, minHeight: 100, overflow: 'hidden', alignSelf: 'center', aspectRatio: 6 }, lottieBackground: { position: 'absolute', width: '100%', maxWidth: 600, minHeight: 100, top: 0, left: 0, right: 0, bottom: 0, zIndex: 0, aspectRatio: 6 }, avatarWrapper: { zIndex: 1, position: 'absolute', alignItems: 'center', justifyContent: 'center', width: 100, height: 100, left: '50%', marginLeft: -50, top: 0 }, nameWrapper: { marginTop: 12, alignItems: 'center', justifyContent: 'center' }, welcomeText: { fontSize: 28, fontWeight: '600', marginBottom: 8, fontFamily: fontFamilies.interBold, maxWidth: '90%' }, welcomeSubtext: { fontSize: 16, fontWeight: '400', opacity: 0.6 }, userIcon: { marginRight: 12 }, manageButton: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 16 // backgroundColor should be applied inline using colors.iconSecurity }, manageButtonText: { color: '#fff', fontSize: 14, fontWeight: '500' }, accountAvatarImage: { width: 40, height: 40, borderRadius: 20 }, accountAvatarFallback: { width: 40, height: 40, borderRadius: 20, backgroundColor: '#d169e5', alignItems: 'center', justifyContent: 'center' }, accountAvatarText: { color: 'white', fontSize: 18, fontWeight: 'bold' }, message: { fontSize: 16, textAlign: 'center', marginTop: 24, color: '#333' }, loadingContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: 20, gap: 12 }, loadingText: { fontSize: 16, color: '#666' } }); export default /*#__PURE__*/React.memo(AccountOverviewScreen); //# sourceMappingURL=AccountOverviewScreen.js.map