UNPKG

@oxyhq/services

Version:

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

450 lines (447 loc) • 15.3 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 _Avatar = _interopRequireDefault(require("../components/Avatar")); var _vectorIcons = require("@expo/vector-icons"); 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 ProfileScreen = ({ userId, username, theme, goBack }) => { const { oxyServices, user: currentUser } = (0, _OxyContext.useOxy)(); const [profile, setProfile] = (0, _react.useState)(null); const [karmaTotal, setKarmaTotal] = (0, _react.useState)(null); const [postsCount, setPostsCount] = (0, _react.useState)(null); const [commentsCount, setCommentsCount] = (0, _react.useState)(null); const [followersCount, setFollowersCount] = (0, _react.useState)(null); const [followingCount, setFollowingCount] = (0, _react.useState)(null); const [isLoading, setIsLoading] = (0, _react.useState)(true); const [error, setError] = (0, _react.useState)(null); const isDarkTheme = theme === 'dark'; const backgroundColor = isDarkTheme ? '#121212' : '#FFFFFF'; const textColor = isDarkTheme ? '#FFFFFF' : '#000000'; const primaryColor = '#d169e5'; (0, _react.useEffect)(() => { console.log('ProfileScreen - userId:', userId); console.log('ProfileScreen - username:', username); if (!userId) { setError('No user ID provided'); setIsLoading(false); return; } setIsLoading(true); setError(null); // Load user profile and karma total Promise.all([oxyServices.getUserById(userId).catch(err => { console.error('getUserById error:', err); // If this is the current user and the API call fails, use current user data as fallback if (currentUser && currentUser.id === userId) { console.log('API call failed, using current user as fallback:', currentUser); return currentUser; } throw err; }), oxyServices.getUserKarmaTotal ? oxyServices.getUserKarmaTotal(userId).catch(err => { console.warn('getUserKarmaTotal error:', err); return { total: undefined }; }) : Promise.resolve({ total: undefined })]).then(([profileRes, karmaRes]) => { console.log('Profile loaded:', profileRes); setProfile(profileRes); setKarmaTotal(typeof karmaRes.total === 'number' ? karmaRes.total : null); // Mock data for other stats // In a real app, these would come from API endpoints setPostsCount(Math.floor(Math.random() * 50)); setCommentsCount(Math.floor(Math.random() * 100)); setFollowersCount(Math.floor(Math.random() * 200)); setFollowingCount(Math.floor(Math.random() * 100)); }).catch(err => { console.error('Profile loading error:', err); // Provide user-friendly error messages based on the error type let errorMessage = 'Failed to load profile'; if (err.status === 404 || err.message?.includes('not found') || err.message?.includes('Resource not found')) { if (currentUser && currentUser.id === userId) { errorMessage = 'Unable to load your profile from the server. This may be due to a temporary service issue.'; } else { errorMessage = 'This user profile could not be found or may have been removed.'; } } else if (err.status === 403) { errorMessage = 'You do not have permission to view this profile.'; } else if (err.status === 500) { errorMessage = 'Server error occurred while loading the profile. Please try again later.'; } else if (err.message) { errorMessage = err.message; } setError(errorMessage); }).finally(() => setIsLoading(false)); }, [userId]); if (isLoading) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.container, { backgroundColor, justifyContent: 'center' }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "large", color: primaryColor }) }); } if (error) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.container, { backgroundColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.errorHeader, children: [goBack && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { onPress: goBack, style: styles.backButton, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "arrow-back", size: 24, color: textColor }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.errorTitle, { color: textColor }], children: "Profile Error" })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.errorContent, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "alert-circle", size: 48, color: "#D32F2F", style: styles.errorIcon }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.errorText, { color: '#D32F2F' }], children: error }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.errorSubtext, { color: textColor }], children: "This could happen if the user doesn't exist or the profile service is unavailable." })] })] }); } return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.container, { backgroundColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, { contentContainerStyle: styles.scrollContainer, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.bannerContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.bannerImage }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.avatarRow, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.avatarWrapper, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.default, { uri: profile?.avatar?.url, name: profile?.username || username, size: 96, theme: theme }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.actionButtonWrapper, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.actionButton, children: "Edit Profile" }) })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.header, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.displayName, { color: textColor }], children: profile?.displayName || profile?.username || username || profile?.id }), profile?.username && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.subText, { color: isDarkTheme ? '#BBBBBB' : '#888888' }], children: ["@", profile.username] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.bio, { color: textColor }], children: profile?.bio || 'This user has no bio yet.' }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.infoRow, children: [profile?.email && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.infoItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "mail-outline", size: 16, color: isDarkTheme ? '#BBBBBB' : '#888888', style: { marginRight: 4 } }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.infoText, { color: isDarkTheme ? '#BBBBBB' : '#888888' }], children: profile.email })] }), profile?.createdAt && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.infoItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.Ionicons, { name: "calendar-outline", size: 16, color: isDarkTheme ? '#BBBBBB' : '#888888', style: { marginRight: 4 } }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: [styles.infoText, { color: isDarkTheme ? '#BBBBBB' : '#888888' }], children: ["Joined ", new Date(profile.createdAt).toLocaleDateString()] })] })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.divider }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.statsRow, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.statItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.karmaAmount, { color: primaryColor }], children: karmaTotal !== null && karmaTotal !== undefined ? karmaTotal : '--' }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.karmaLabel, { color: isDarkTheme ? '#BBBBBB' : '#888888' }], children: "Karma" })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.statItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.karmaAmount, { color: textColor }], children: followersCount !== null ? followersCount : '--' }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.karmaLabel, { color: isDarkTheme ? '#BBBBBB' : '#888888' }], children: "Followers" })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.statItem, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.karmaAmount, { color: textColor }], children: followingCount !== null ? followingCount : '--' }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.karmaLabel, { color: isDarkTheme ? '#BBBBBB' : '#888888' }], children: "Following" })] })] })] })] }) }); }; const styles = _reactNative.StyleSheet.create({ container: { flex: 1 }, scrollContainer: { alignItems: 'stretch', paddingBottom: 40 }, bannerContainer: { height: 160, backgroundColor: '#e1bee7', position: 'relative', overflow: 'hidden' }, bannerImage: { flex: 1, backgroundColor: '#d169e5' }, // Placeholder, replace with Image if available avatarRow: { flexDirection: 'row', alignItems: 'flex-end', marginTop: -56, paddingHorizontal: 20, justifyContent: 'space-between', zIndex: 2 }, avatarWrapper: { borderWidth: 5, borderColor: '#fff', borderRadius: 64, overflow: 'hidden', backgroundColor: '#fff' }, actionButtonWrapper: { flex: 1, alignItems: 'flex-end', justifyContent: 'flex-end' }, actionButton: { backgroundColor: '#fff', color: '#d169e5', borderWidth: 1, borderColor: '#d169e5', borderRadius: 24, paddingVertical: 7, paddingHorizontal: 22, fontWeight: 'bold', fontSize: 16, marginBottom: 8, elevation: 2, shadowColor: '#d169e5', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.08, shadowRadius: 2 }, header: { alignItems: 'flex-start', paddingTop: 18, paddingBottom: 24, width: '100%', paddingHorizontal: 20 }, displayName: { fontSize: 24, fontWeight: 'bold', marginTop: 10, marginBottom: 2, letterSpacing: 0.1 }, subText: { fontSize: 16, marginBottom: 2, color: '#a0a0a0' }, bio: { fontSize: 16, marginTop: 10, marginBottom: 10, color: '#666', fontStyle: 'italic', lineHeight: 22 }, infoRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 10, flexWrap: 'wrap' }, infoItem: { flexDirection: 'row', alignItems: 'center', marginRight: 28, marginBottom: 4, minWidth: 120 }, infoText: { fontSize: 15 }, divider: { height: 1, backgroundColor: '#e0e0e0', width: '100%', marginVertical: 14 }, statsRow: { width: '100%', flex: 1, flexDirection: 'row', alignItems: 'center', marginTop: 6, marginBottom: 2, justifyContent: 'space-between' }, statItem: { flex: 1, alignItems: 'center', minWidth: 50, marginBottom: 12 }, karmaLabel: { fontSize: 14, marginBottom: 2, textAlign: 'center', color: '#a0a0a0' }, karmaAmount: { fontSize: 24, fontWeight: 'bold', textAlign: 'center', letterSpacing: 0.2 }, // Error handling styles errorHeader: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#E0E0E0' }, backButton: { padding: 8, marginRight: 16 }, errorTitle: { fontSize: 20, fontWeight: 'bold' }, errorContent: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 32 }, errorIcon: { marginBottom: 16 }, errorText: { fontSize: 18, fontWeight: '600', textAlign: 'center', marginBottom: 8 }, errorSubtext: { fontSize: 14, textAlign: 'center', opacity: 0.7 } }); var _default = exports.default = ProfileScreen; //# sourceMappingURL=ProfileScreen.js.map