@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
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 _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