@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
664 lines (649 loc) • 25.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _OxyContext = require("../context/OxyContext");
var _fonts = require("../styles/fonts");
var _version = require("../../constants/version");
var _sonner = require("../../lib/sonner");
var _OxyIcon = _interopRequireDefault(require("../components/icon/OxyIcon"));
var _vectorIcons = require("@expo/vector-icons");
var _OxyServices = _interopRequireDefault(require("../../assets/icons/OxyServices"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const AppInfoScreen = ({
onClose,
theme,
navigate
}) => {
const {
user,
sessions,
oxyServices
} = (0, _OxyContext.useOxy)();
const [systemInfo, setSystemInfo] = (0, _react.useState)(null);
const [isRunningSystemCheck, setIsRunningSystemCheck] = (0, _react.useState)(false);
const [connectionStatus, setConnectionStatus] = (0, _react.useState)('unknown');
const isDarkTheme = theme === 'dark';
const backgroundColor = isDarkTheme ? '#121212' : '#f2f2f2';
const primaryColor = '#007AFF';
(0, _react.useEffect)(() => {
const updateDimensions = () => {
const dimensions = _reactNative.Dimensions.get('window');
setSystemInfo(prev => ({
...prev,
platform: _reactNative.Platform.OS,
version: _reactNative.Platform.Version?.toString() || 'Unknown',
screenDimensions: {
width: dimensions.width,
height: dimensions.height
},
timestamp: new Date().toISOString()
}));
};
// Set initial dimensions
updateDimensions();
// Listen for dimension changes
const subscription = _reactNative.Dimensions.addEventListener('change', updateDimensions);
// Check API connection on mount
const checkConnection = async () => {
setConnectionStatus('checking');
const apiBaseUrl = oxyServices?.getBaseURL() || 'https://api.oxy.so';
try {
const response = await fetch(`${apiBaseUrl}/`, {
method: 'GET',
timeout: 3000
});
if (response.ok) {
setConnectionStatus('connected');
} else {
setConnectionStatus('disconnected');
}
} catch (error) {
setConnectionStatus('disconnected');
}
};
checkConnection();
// Cleanup listener on unmount
return () => {
subscription?.remove();
};
}, []);
const copyToClipboard = async (text, label) => {
try {
await _reactNative.Clipboard.setString(text);
_sonner.toast.success(`${label} copied to clipboard`);
} catch (error) {
_sonner.toast.error('Failed to copy to clipboard');
}
};
const runSystemCheck = async () => {
if (!oxyServices) {
_sonner.toast.error('OxyServices not initialized');
return;
}
setIsRunningSystemCheck(true);
const checks = [];
// Get the API base URL from the services instance
const apiBaseUrl = oxyServices?.getBaseURL() || 'https://api.oxy.so'; // Default for now, could be made configurable
try {
// Check 1: API Server Health
checks.push('🔍 Checking API server connection...');
_sonner.toast.info('Running system checks...', {
duration: 2000
});
try {
const response = await fetch(`${apiBaseUrl}/`, {
method: 'GET',
timeout: 5000
});
if (response.ok) {
const data = await response.json();
checks.push('✅ API server is responding');
checks.push(`📊 Server stats: ${data.users || 0} users`);
checks.push(`🌐 API URL: ${apiBaseUrl}`);
setConnectionStatus('connected');
} else {
checks.push('❌ API server returned error status');
checks.push(` Status: ${response.status} ${response.statusText}`);
setConnectionStatus('disconnected');
}
} catch (error) {
checks.push('❌ API server connection failed');
checks.push(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
checks.push(` URL: ${apiBaseUrl}`);
setConnectionStatus('disconnected');
}
// Check 2: Authentication Status
checks.push('🔍 Checking authentication...');
if (oxyServices.isAuthenticated()) {
checks.push('✅ User is authenticated');
// Check 3: Token Validation
try {
const isValid = await oxyServices.validate();
if (isValid) {
checks.push('✅ Authentication token is valid');
} else {
checks.push('❌ Authentication token is invalid');
}
} catch (error) {
checks.push('❌ Token validation failed');
checks.push(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
} else {
checks.push('⚠️ User is not authenticated');
}
// Check 4: Session Validation (if user has active sessions)
if (user && sessions && sessions.length > 0) {
checks.push('🔍 Checking active sessions...');
try {
// Just check if we can fetch sessions
const userSessions = await oxyServices.getUserSessions();
checks.push(`✅ Session validation successful (${userSessions.length} sessions)`);
} catch (error) {
checks.push('❌ Session validation failed');
checks.push(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Check 5: Platform Information
checks.push('🔍 Checking platform information...');
checks.push(`✅ Platform: ${_reactNative.Platform.OS} ${_reactNative.Platform.Version || 'Unknown'}`);
checks.push(`✅ Screen: ${systemInfo?.screenDimensions.width || 0}x${systemInfo?.screenDimensions.height || 0}`);
checks.push(`✅ Environment: ${__DEV__ ? 'Development' : 'Production'}`);
// Check 6: Package Information
checks.push('🔍 Checking package information...');
checks.push(`✅ Package: ${_version.packageInfo.name}@${_version.packageInfo.version}`);
// Check 7: Memory and Performance (basic)
checks.push('🔍 Checking performance metrics...');
const memoryUsage = performance.memory;
if (memoryUsage) {
const usedMB = Math.round(memoryUsage.usedJSHeapSize / 1024 / 1024);
const totalMB = Math.round(memoryUsage.totalJSHeapSize / 1024 / 1024);
checks.push(`✅ Memory usage: ${usedMB}MB / ${totalMB}MB`);
} else {
checks.push('✅ Performance metrics not available on this platform');
}
// Final summary
const errorCount = checks.filter(check => check.includes('❌')).length;
const warningCount = checks.filter(check => check.includes('⚠️')).length;
checks.push('');
checks.push('📋 SYSTEM CHECK SUMMARY:');
if (errorCount === 0 && warningCount === 0) {
checks.push('✅ All systems operational');
_sonner.toast.success('System check completed - All systems operational!');
} else if (errorCount === 0) {
checks.push(`⚠️ ${warningCount} warning(s) found`);
_sonner.toast.warning(`System check completed with ${warningCount} warning(s)`);
} else {
checks.push(`❌ ${errorCount} error(s) and ${warningCount} warning(s) found`);
_sonner.toast.error(`System check failed with ${errorCount} error(s)`);
}
// Show results in an alert and copy to clipboard
const report = checks.join('\n');
_reactNative.Alert.alert('System Check Results', `Check completed. Results copied to clipboard.\n\nSummary: ${errorCount} errors, ${warningCount} warnings`, [{
text: 'View Full Report',
onPress: () => copyToClipboard(report, 'System check report')
}, {
text: 'OK',
style: 'default'
}]);
} catch (error) {
_sonner.toast.error('System check failed to run');
console.error('System check error:', error);
} finally {
setIsRunningSystemCheck(false);
}
};
const generateFullReport = () => {
const report = {
packageInfo: {
name: _version.packageInfo.name,
version: _version.packageInfo.version,
description: _version.packageInfo.description
},
systemInfo,
userInfo: {
isAuthenticated: !!user,
userId: user?.id || 'Not authenticated',
username: user?.username || 'N/A',
totalUsers: sessions?.length || 0
},
apiConfiguration: {
apiUrl: oxyServices?.getBaseURL() || 'Not configured'
},
buildInfo: {
timestamp: new Date().toISOString(),
environment: __DEV__ ? 'Development' : 'Production'
}
};
return JSON.stringify(report, null, 2);
};
const handleCopyFullReport = () => {
const report = generateFullReport();
copyToClipboard(report, 'Full application report');
};
const InfoRow = ({
label,
value,
copyable = false,
icon = 'information-circle',
iconComponent,
color = '#8E8E93',
isFirst = false,
isLast = false,
onPress,
showChevron = false
}) => {
const handlePress = () => {
if (onPress) {
onPress();
} else if (copyable) {
copyToClipboard(value, label);
}
};
const isInteractive = copyable || !!onPress;
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
style: [styles.settingItem, isFirst && styles.firstSettingItem, isLast && styles.lastSettingItem],
onPress: isInteractive ? handlePress : undefined,
disabled: !isInteractive,
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.settingInfo,
children: [iconComponent ? (/*#__PURE__*/_react.default.cloneElement(iconComponent, {
style: styles.settingIcon
})) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: icon,
size: 20,
color: color,
style: styles.settingIcon
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.settingDetails,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.settingLabel,
children: label
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: [styles.settingValue, (copyable || onPress) && {
color: primaryColor
}],
children: value
})]
})]
}), copyable && /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: "copy",
size: 16,
color: "#ccc"
}), showChevron && /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: "chevron-forward",
size: 16,
color: "#ccc"
})]
});
};
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: [styles.container, {
backgroundColor
}],
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.header,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
style: styles.cancelButton,
onPress: onClose,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, {
name: "close",
size: 24,
color: "#666"
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.headerTitle,
children: "App Information"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: styles.placeholder
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
style: styles.content,
showsVerticalScrollIndicator: false,
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.section,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.sectionTitle,
children: "Package Information"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Name",
value: _version.packageInfo.name,
copyable: true,
iconComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyServices.default, {
width: 20,
height: 20
}),
color: "#007AFF",
isFirst: true
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Version",
value: _version.packageInfo.version,
copyable: true,
icon: "pricetag",
color: "#5856D6"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Description",
value: _version.packageInfo.description || 'No description',
icon: "document-text",
color: "#34C759"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Main Entry",
value: _version.packageInfo.main || 'N/A',
icon: "code",
color: "#FF9500"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Module Entry",
value: _version.packageInfo.module || 'N/A',
icon: "library",
color: "#FF3B30"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Types Entry",
value: _version.packageInfo.types || 'N/A',
icon: "construct",
color: "#32D74B",
isLast: true
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.section,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.sectionTitle,
children: "System Information"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Platform",
value: _reactNative.Platform.OS,
icon: "phone-portrait",
color: "#007AFF",
isFirst: true
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Platform Version",
value: systemInfo?.version || 'Loading...',
icon: "hardware-chip",
color: "#5856D6"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Screen Width",
value: `${systemInfo?.screenDimensions.width || 0}px`,
icon: "resize",
color: "#FF9500"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Screen Height",
value: `${systemInfo?.screenDimensions.height || 0}px`,
icon: "resize",
color: "#FF3B30"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Environment",
value: __DEV__ ? 'Development' : 'Production',
icon: "settings",
color: "#34C759",
isLast: true
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.section,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.sectionTitle,
children: "User Information"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Authentication Status",
value: user ? 'Authenticated' : 'Not Authenticated',
icon: "shield-checkmark",
color: user ? '#34C759' : '#FF3B30',
isFirst: true
}), user && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "User ID",
value: user.id,
copyable: true,
icon: "person",
color: "#007AFF"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Username",
value: user.username || 'N/A',
icon: "at",
color: "#5856D6",
onPress: () => {
if (user?.username && navigate) {
navigate('Profile', {
userId: user.id
});
} else {
_sonner.toast.info('No username available or navigation not supported');
}
},
showChevron: true
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Email",
value: user.email || 'N/A',
icon: "mail",
color: "#FF9500"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Premium Status",
value: user.isPremium ? 'Premium' : 'Standard',
icon: "star",
color: user.isPremium ? '#FFD700' : '#8E8E93'
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Total Active Sessions",
value: sessions?.length?.toString() || '0',
icon: "people",
color: "#32D74B",
isLast: true
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.section,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.sectionTitle,
children: "API Configuration"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "API Base URL",
value: oxyServices?.getBaseURL() || 'Not configured',
copyable: true,
icon: "server",
color: "#007AFF",
isFirst: true
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Connection Status",
value: connectionStatus === 'checking' ? 'Checking...' : connectionStatus === 'connected' ? 'Connected' : connectionStatus === 'disconnected' ? 'Disconnected' : 'Unknown',
icon: connectionStatus === 'checking' ? 'sync' : connectionStatus === 'connected' ? 'wifi' : 'wifi-off',
color: connectionStatus === 'checking' ? '#FF9500' : connectionStatus === 'connected' ? '#34C759' : '#FF3B30',
onPress: async () => {
setConnectionStatus('checking');
const apiBaseUrl = oxyServices?.getBaseURL() || 'https://api.oxy.so';
try {
const response = await fetch(`${apiBaseUrl}/`, {
method: 'GET',
timeout: 3000
});
if (response.ok) {
setConnectionStatus('connected');
_sonner.toast.success('API connection successful');
} else {
setConnectionStatus('disconnected');
_sonner.toast.error(`API server error: ${response.status}`);
}
} catch (error) {
setConnectionStatus('disconnected');
_sonner.toast.error('Failed to connect to API server');
}
},
showChevron: true,
isLast: true
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.section,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.sectionTitle,
children: "Build Information"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "Build Timestamp",
value: systemInfo?.timestamp || 'Loading...',
copyable: true,
icon: "time",
color: "#007AFF",
isFirst: true
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "React Native",
value: "Expo/React Native",
icon: "logo-react",
color: "#61DAFB"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
label: "JavaScript Engine",
value: "Hermes",
icon: "flash",
color: "#FF3B30",
isLast: true
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.section,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.sectionTitle,
children: "Quick Actions"
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
style: [styles.settingItem, styles.firstSettingItem],
onPress: handleCopyFullReport,
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.settingInfo,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: "copy",
size: 20,
color: "#007AFF",
style: styles.settingIcon
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.settingDetails,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.settingLabel,
children: "Copy Full Report"
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.settingDescription,
children: "Copy complete application information to clipboard"
})]
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: "chevron-forward",
size: 16,
color: "#ccc"
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
style: [styles.settingItem, styles.lastSettingItem, isRunningSystemCheck && styles.disabledSettingItem],
onPress: runSystemCheck,
disabled: isRunningSystemCheck,
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.settingInfo,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: isRunningSystemCheck ? "sync" : "checkmark-circle",
size: 20,
color: isRunningSystemCheck ? "#FF9500" : "#34C759",
style: [styles.settingIcon, isRunningSystemCheck && styles.spinningIcon]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: styles.settingDetails,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.settingLabel,
children: isRunningSystemCheck ? 'Running System Check...' : 'Run System Check'
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: styles.settingDescription,
children: isRunningSystemCheck ? 'Checking API, authentication, and platform status...' : 'Verify application health and status'
})]
})]
}), !isRunningSystemCheck && /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyIcon.default, {
name: "chevron-forward",
size: 16,
color: "#ccc"
})]
})]
})]
})]
});
};
const styles = _reactNative.StyleSheet.create({
container: {
flex: 1
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#fff'
},
cancelButton: {
padding: 5
},
headerTitle: {
fontSize: 24,
fontWeight: 'bold',
color: '#000',
fontFamily: _fonts.fontFamilies.phuduBold
},
placeholder: {
width: 34 // Same width as cancel button to center title
},
content: {
flex: 1,
padding: 16
},
section: {
marginBottom: 24
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 12,
fontFamily: _fonts.fontFamilies.phuduSemiBold
},
settingItem: {
backgroundColor: '#fff',
padding: 16,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 2
},
firstSettingItem: {
borderTopLeftRadius: 24,
borderTopRightRadius: 24
},
lastSettingItem: {
borderBottomLeftRadius: 24,
borderBottomRightRadius: 24,
marginBottom: 8
},
settingInfo: {
flexDirection: 'row',
alignItems: 'center',
flex: 1
},
settingIcon: {
marginRight: 12
},
settingDetails: {
flex: 1
},
settingLabel: {
fontSize: 16,
fontWeight: '500',
color: '#333',
marginBottom: 2
},
settingValue: {
fontSize: 14,
color: '#666'
},
settingDescription: {
fontSize: 14,
color: '#999'
},
disabledSettingItem: {
opacity: 0.6
},
spinningIcon: {
// Note: Animation would need to be implemented with Animated API
// For now, just showing the sync icon to indicate loading
}
});
var _default = exports.default = AppInfoScreen;
//# sourceMappingURL=AppInfoScreen.js.map