UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

449 lines (446 loc) • 15.8 kB
"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 _sonner = require("../../lib/sonner"); var _jsxRuntime = require("react/jsx-runtime"); 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 SessionManagementScreen = ({ onClose, theme }) => { const { sessions: userSessions, activeSessionId, refreshSessions, logout, oxyServices } = (0, _OxyContext.useOxy)(); const [loading, setLoading] = (0, _react.useState)(true); const [refreshing, setRefreshing] = (0, _react.useState)(false); const [actionLoading, setActionLoading] = (0, _react.useState)(null); const isDarkTheme = theme === 'dark'; const textColor = isDarkTheme ? '#FFFFFF' : '#000000'; const backgroundColor = isDarkTheme ? '#121212' : '#FFFFFF'; const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#F5F5F5'; const borderColor = isDarkTheme ? '#444444' : '#E0E0E0'; const primaryColor = '#0066CC'; const dangerColor = '#D32F2F'; const successColor = '#2E7D32'; const loadSessions = async (isRefresh = false) => { try { if (isRefresh) { setRefreshing(true); } else { setLoading(true); } await refreshSessions(); } catch (error) { console.error('Failed to load sessions:', error); _reactNative.Alert.alert('Error', 'Failed to load sessions. Please try again.', [{ text: 'OK' }]); } finally { setLoading(false); setRefreshing(false); } }; const handleLogoutSession = async sessionId => { _reactNative.Alert.alert('Logout Session', 'Are you sure you want to logout this session?', [{ text: 'Cancel', style: 'cancel' }, { text: 'Logout', style: 'destructive', onPress: async () => { try { setActionLoading(sessionId); await logout(sessionId); // Refresh sessions to update the list await refreshSessions(); _sonner.toast.success('Session logged out successfully'); } catch (error) { console.error('Logout session failed:', error); _sonner.toast.error('Failed to logout session. Please try again.'); } finally { setActionLoading(null); } } }]); }; const handleLogoutOtherSessions = async () => { const otherSessionsCount = userSessions.filter(s => s.sessionId !== activeSessionId).length; if (otherSessionsCount === 0) { _sonner.toast.info('No other sessions to logout.'); return; } _reactNative.Alert.alert('Logout Other Sessions', `This will logout ${otherSessionsCount} other session${otherSessionsCount > 1 ? 's' : ''}. Continue?`, [{ text: 'Cancel', style: 'cancel' }, { text: 'Logout Others', style: 'destructive', onPress: async () => { try { setActionLoading('others'); // Logout each non-active session for (const session of userSessions) { if (session.sessionId !== activeSessionId) { await logout(session.sessionId); } } // Refresh sessions to update the list await refreshSessions(); _sonner.toast.success('Other sessions logged out successfully'); } catch (error) { console.error('Logout other sessions failed:', error); _sonner.toast.error('Failed to logout other sessions. Please try again.'); } finally { setActionLoading(null); } } }]); }; const handleLogoutAllSessions = async () => { _reactNative.Alert.alert('Logout All Sessions', 'This will logout all sessions including this one and you will need to sign in again. Continue?', [{ text: 'Cancel', style: 'cancel' }, { text: 'Logout All', style: 'destructive', onPress: async () => { try { setActionLoading('all'); await oxyServices.logoutAllSessions(); // No need to update local state as user will be logged out } catch (error) { console.error('Logout all sessions failed:', error); _sonner.toast.error('Failed to logout all sessions. Please try again.'); setActionLoading(null); } } }]); }; const formatDate = dateString => { const date = new Date(dateString); const now = new Date(); const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / (1000 * 60)); if (diffInMinutes < 1) return 'Just now'; if (diffInMinutes < 60) return `${diffInMinutes}m ago`; if (diffInMinutes < 1440) return `${Math.floor(diffInMinutes / 60)}h ago`; if (diffInMinutes < 10080) return `${Math.floor(diffInMinutes / 1440)}d ago`; return date.toLocaleDateString(); }; const getDeviceIcon = (deviceType, platform) => { if (platform.toLowerCase().includes('ios') || platform.toLowerCase().includes('iphone')) { return '📱'; } if (platform.toLowerCase().includes('android')) { return '📱'; } if (deviceType.toLowerCase().includes('mobile')) { return '📱'; } if (deviceType.toLowerCase().includes('tablet')) { return '📱'; } return '💻'; // Desktop/web }; (0, _react.useEffect)(() => { loadSessions(); }, []); if (loading) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.container, styles.centerContent, { backgroundColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "large", color: primaryColor }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.loadingText, { color: textColor }], children: "Loading sessions..." })] }); } 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.Text, { style: [styles.title, { color: textColor }], children: "Active Sessions" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.subtitle, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "Manage your active sessions across all devices" })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, { style: styles.scrollView, contentContainerStyle: styles.scrollContainer, refreshControl: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.RefreshControl, { refreshing: refreshing, onRefresh: () => loadSessions(true), tintColor: primaryColor }), children: userSessions.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [userSessions.map(session => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.sessionCard, { backgroundColor: secondaryBackgroundColor, borderColor, borderLeftColor: session.sessionId === activeSessionId ? successColor : borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.sessionHeader, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.sessionTitleRow, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.deviceIcon, children: "\uD83D\uDCF1" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.sessionTitleText, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.deviceName, { color: textColor }], children: ["Session ", session.sessionId.substring(0, 8), "..."] }), session.sessionId === activeSessionId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.currentBadge, { color: successColor }], children: "Current Session" })] })] }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.sessionDetails, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.sessionDetail, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["Device ID: ", session.deviceId.substring(0, 12), "..."] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.sessionDetail, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["Last active: ", new Date(session.lastActive).toLocaleDateString()] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.sessionDetail, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: ["Expires: ", new Date(session.expiresAt).toLocaleDateString()] })] }), session.sessionId !== activeSessionId && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.logoutButton, { backgroundColor: isDarkTheme ? '#400000' : '#FFEBEE' }], onPress: () => handleLogoutSession(session.sessionId), disabled: actionLoading === session.sessionId, children: actionLoading === session.sessionId ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "small", color: dangerColor }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.logoutButtonText, { color: dangerColor }], children: "Logout" }) })] }, session.sessionId)), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.bulkActions, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.bulkActionButton, { backgroundColor: isDarkTheme ? '#1A1A1A' : '#F0F0F0', borderColor }], onPress: handleLogoutOtherSessions, disabled: actionLoading === 'others' || userSessions.filter(s => s.sessionId !== activeSessionId).length === 0, children: actionLoading === 'others' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "small", color: primaryColor }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.bulkActionButtonText, { color: textColor }], children: "Logout Other Sessions" }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: [styles.bulkActionButton, styles.dangerButton, { backgroundColor: isDarkTheme ? '#400000' : '#FFEBEE' }], onPress: handleLogoutAllSessions, disabled: actionLoading === 'all', children: actionLoading === 'all' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "small", color: dangerColor }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.bulkActionButtonText, { color: dangerColor }], children: "Logout All Sessions" }) })] })] }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.emptyState, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.emptyStateText, { color: isDarkTheme ? '#BBBBBB' : '#666666' }], children: "No active sessions found" }) }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.footer, { borderTopColor: borderColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { style: styles.closeButton, onPress: onClose, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.closeButtonText, { color: primaryColor }], children: "Close" }) }) })] }); }; const styles = _reactNative.StyleSheet.create({ container: { flex: 1 }, centerContent: { justifyContent: 'center', alignItems: 'center' }, header: { padding: 20, paddingBottom: 16 }, title: { fontFamily: _reactNative.Platform.OS === 'web' ? 'Phudu' : 'phuduSemiBold', fontWeight: _reactNative.Platform.OS === 'web' ? '600' : undefined, fontSize: 24, marginBottom: 8 }, subtitle: { fontSize: 16, lineHeight: 20 }, scrollView: { flex: 1 }, scrollContainer: { padding: 20, paddingTop: 0 }, sessionCard: { borderRadius: 12, borderWidth: 1, borderLeftWidth: 4, padding: 16, marginBottom: 12 }, sessionHeader: { marginBottom: 12 }, sessionTitleRow: { flexDirection: 'row', alignItems: 'center' }, deviceIcon: { fontSize: 20, marginRight: 12 }, sessionTitleText: { flex: 1 }, deviceName: { fontSize: 16, fontWeight: '600', marginBottom: 2 }, currentBadge: { fontSize: 12, fontWeight: '500' }, sessionDetails: { marginBottom: 12 }, sessionDetail: { fontSize: 14, marginBottom: 2 }, logoutButton: { paddingVertical: 8, paddingHorizontal: 16, borderRadius: 6, alignItems: 'center', alignSelf: 'flex-start' }, logoutButtonText: { fontSize: 14, fontWeight: '500' }, bulkActions: { marginTop: 20, paddingTop: 20, borderTopWidth: 1, borderTopColor: '#E0E0E0' }, bulkActionButton: { paddingVertical: 12, paddingHorizontal: 20, borderRadius: 8, borderWidth: 1, alignItems: 'center', marginBottom: 12 }, dangerButton: { borderColor: 'transparent' }, bulkActionButtonText: { fontSize: 16, fontWeight: '500' }, emptyState: { alignItems: 'center', paddingVertical: 40 }, emptyStateText: { fontSize: 16, fontStyle: 'italic' }, loadingText: { fontSize: 16, marginTop: 16 }, footer: { padding: 16, borderTopWidth: 1, alignItems: 'center' }, closeButton: { paddingVertical: 8, paddingHorizontal: 16 }, closeButtonText: { fontSize: 16, fontWeight: '600' } }); var _default = exports.default = SessionManagementScreen; //# sourceMappingURL=SessionManagementScreen.js.map