UNPKG

@oxyhq/services

Version:

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

545 lines (540 loc) • 21.9 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 _components = require("../components"); var _useI18n = require("../hooks/useI18n"); 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 PrivacySettingsScreen = ({ onClose, theme, goBack }) => { const { oxyServices, user } = (0, _OxyContext.useOxy)(); const { t } = (0, _useI18n.useI18n)(); const [settings, setSettings] = (0, _react.useState)({ isPrivateAccount: false, hideOnlineStatus: false, hideLastSeen: false, profileVisibility: true, twoFactorEnabled: false, loginAlerts: true, blockScreenshots: false, login: true, biometricLogin: false, showActivity: true, allowTagging: true, allowMentions: true, hideReadReceipts: false, allowDirectMessages: true, dataSharing: true, locationSharing: false, analyticsSharing: true, sensitiveContent: false, autoFilter: true, muteKeywords: false }); const [isLoading, setIsLoading] = (0, _react.useState)(true); const [isSaving, setIsSaving] = (0, _react.useState)(false); const [blockedUsers, setBlockedUsers] = (0, _react.useState)([]); const [restrictedUsers, setRestrictedUsers] = (0, _react.useState)([]); const [isLoadingUsers, setIsLoadingUsers] = (0, _react.useState)(false); // Load settings and users (0, _react.useEffect)(() => { const loadSettings = async () => { try { setIsLoading(true); if (user?.id && oxyServices) { const privacySettings = await oxyServices.getPrivacySettings(user.id); if (privacySettings) { setSettings(privacySettings); } } } catch (error) { console.error('Failed to load privacy settings:', error); _sonner.toast.error(t('privacySettings.loadError') || 'Failed to load privacy settings'); } finally { setIsLoading(false); } }; loadSettings(); }, [user?.id, oxyServices, t]); // Load blocked and restricted users (0, _react.useEffect)(() => { const loadUsers = async () => { if (!oxyServices) return; try { setIsLoadingUsers(true); const [blocked, restricted] = await Promise.all([oxyServices.getBlockedUsers(), oxyServices.getRestrictedUsers()]); setBlockedUsers(blocked); setRestrictedUsers(restricted); } catch (error) { console.error('Failed to load blocked/restricted users:', error); } finally { setIsLoadingUsers(false); } }; loadUsers(); }, [oxyServices]); const updateSetting = (0, _react.useCallback)(async (key, value) => { try { setIsSaving(true); const newSettings = { ...settings, [key]: value }; setSettings(newSettings); if (user?.id && oxyServices) { await oxyServices.updatePrivacySettings({ [key]: value }, user.id); _sonner.toast.success(t('privacySettings.updated') || 'Privacy settings updated'); } } catch (error) { console.error(`Failed to update ${key}:`, error); _sonner.toast.error(t('privacySettings.updateError') || 'Failed to update privacy setting'); // Revert on error setSettings(settings); } finally { setIsSaving(false); } }, [settings, user?.id, oxyServices, t]); const handleUnblock = (0, _react.useCallback)(async userId => { if (!oxyServices) return; try { await oxyServices.unblockUser(userId); setBlockedUsers(prev => prev.filter(u => { const id = typeof u.blockedId === 'string' ? u.blockedId : u.blockedId._id; return id !== userId; })); _sonner.toast.success(t('privacySettings.userUnblocked') || 'User unblocked'); } catch (error) { console.error('Failed to unblock user:', error); _sonner.toast.error(t('privacySettings.unblockError') || 'Failed to unblock user'); } }, [oxyServices, t]); const handleUnrestrict = (0, _react.useCallback)(async userId => { if (!oxyServices) return; try { await oxyServices.unrestrictUser(userId); setRestrictedUsers(prev => prev.filter(u => { const id = typeof u.restrictedId === 'string' ? u.restrictedId : u.restrictedId._id; return id !== userId; })); _sonner.toast.success(t('privacySettings.userUnrestricted') || 'User unrestricted'); } catch (error) { console.error('Failed to unrestrict user:', error); _sonner.toast.error(t('privacySettings.unrestrictError') || 'Failed to unrestrict user'); } }, [oxyServices, t]); // Helper to extract user info from blocked/restricted objects const extractUserInfo = (0, _react.useCallback)((item, idField) => { let userIdField; let username; let avatar; if (idField === 'blockedId' && 'blockedId' in item) { userIdField = item.blockedId; username = typeof item.blockedId === 'string' ? item.username || 'Unknown' : item.blockedId.username || 'Unknown'; avatar = typeof item.blockedId === 'string' ? item.avatar : item.blockedId.avatar; } else if (idField === 'restrictedId' && 'restrictedId' in item) { userIdField = item.restrictedId; username = typeof item.restrictedId === 'string' ? item.username || 'Unknown' : item.restrictedId.username || 'Unknown'; avatar = typeof item.restrictedId === 'string' ? item.avatar : item.restrictedId.avatar; } else { // Fallback (should not happen) return { userId: '', username: 'Unknown', avatar: undefined }; } const userId = typeof userIdField === 'string' ? userIdField : userIdField._id; return { userId, username, avatar }; }, []); // Reusable user list item component const UserListItem = ({ item, idField, onAction, actionLabel, actionColor, subtitle }) => { const { userId, username, avatar } = extractUserInfo(item, idField); // Convert avatar file ID to URI if needed const avatarUri = avatar && oxyServices ? oxyServices.getFileDownloadUrl(avatar, 'thumb') : undefined; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.userRow, { borderBottomColor: themeStyles.borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.userInfo, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Avatar, { uri: avatarUri, name: username, size: 40, theme: theme }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.userDetails, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.username, { color: themeStyles.textColor }], children: username }), subtitle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.userSubtext, { color: themeStyles.mutedTextColor }], children: subtitle })] })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { onPress: () => onAction(userId), style: [styles.actionButton, { backgroundColor: themeStyles.secondaryBackgroundColor }], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.actionButtonText, { color: actionColor }], children: actionLabel }) })] }); }; const themeStyles = (0, _react.useMemo)(() => { const isDarkTheme = theme === 'dark'; return { textColor: isDarkTheme ? '#FFFFFF' : '#000000', backgroundColor: isDarkTheme ? '#121212' : '#FFFFFF', secondaryBackgroundColor: isDarkTheme ? '#222222' : '#F5F5F5', borderColor: isDarkTheme ? '#444444' : '#E0E0E0', mutedTextColor: isDarkTheme ? '#8E8E93' : '#8E8E93' }; }, [theme]); const SettingRow = ({ title, description, value, onValueChange, disabled }) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.settingRow, { borderBottomColor: themeStyles.borderColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.settingInfo, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.settingTitle, { color: themeStyles.textColor }], children: title }), description && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.settingDescription, { color: themeStyles.mutedTextColor }], children: description })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, { value: value, onValueChange: onValueChange, disabled: disabled || isSaving, trackColor: { false: '#767577', true: '#d169e5' }, thumbColor: value ? '#fff' : '#f4f3f4' })] }); if (isLoading) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.container, { backgroundColor: themeStyles.backgroundColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Header, { title: t('privacySettings.title') || 'Privacy Settings', theme: theme, onBack: goBack || onClose, variant: "minimal", elevation: "subtle" }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.loadingContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "large", color: themeStyles.textColor }) })] }); } return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: [styles.container, { backgroundColor: themeStyles.backgroundColor }], children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Header, { title: t('privacySettings.title') || 'Privacy Settings', theme: theme, onBack: goBack || onClose, variant: "minimal", elevation: "subtle" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, { style: styles.content, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Section, { title: t('privacySettings.sections.account') || 'ACCOUNT PRIVACY', theme: theme, isFirst: true, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.isPrivateAccount') || 'Private Account', description: t('privacySettings.isPrivateAccountDesc') || 'Only approved followers can see your posts', value: settings.isPrivateAccount, onValueChange: value => updateSetting('isPrivateAccount', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.profileVisibility') || 'Profile Visibility', description: t('privacySettings.profileVisibilityDesc') || 'Control who can view your profile', value: settings.profileVisibility, onValueChange: value => updateSetting('profileVisibility', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.hideOnlineStatus') || 'Hide Online Status', description: t('privacySettings.hideOnlineStatusDesc') || 'Don\'t show when you\'re online', value: settings.hideOnlineStatus, onValueChange: value => updateSetting('hideOnlineStatus', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.hideLastSeen') || 'Hide Last Seen', description: t('privacySettings.hideLastSeenDesc') || 'Don\'t show when you were last active', value: settings.hideLastSeen, onValueChange: value => updateSetting('hideLastSeen', value) })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Section, { title: t('privacySettings.sections.interactions') || 'INTERACTIONS', theme: theme, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.allowTagging') || 'Allow Tagging', description: t('privacySettings.allowTaggingDesc') || 'Let others tag you in posts', value: settings.allowTagging, onValueChange: value => updateSetting('allowTagging', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.allowMentions') || 'Allow Mentions', description: t('privacySettings.allowMentionsDesc') || 'Let others mention you', value: settings.allowMentions, onValueChange: value => updateSetting('allowMentions', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.allowDirectMessages') || 'Allow Direct Messages', description: t('privacySettings.allowDirectMessagesDesc') || 'Let others send you direct messages', value: settings.allowDirectMessages, onValueChange: value => updateSetting('allowDirectMessages', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.hideReadReceipts') || 'Hide Read Receipts', description: t('privacySettings.hideReadReceiptsDesc') || 'Don\'t show read receipts in messages', value: settings.hideReadReceipts, onValueChange: value => updateSetting('hideReadReceipts', value) })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Section, { title: t('privacySettings.sections.activity') || 'ACTIVITY & DATA', theme: theme, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.showActivity') || 'Show Activity Status', description: t('privacySettings.showActivityDesc') || 'Display your activity on your profile', value: settings.showActivity, onValueChange: value => updateSetting('showActivity', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.dataSharing') || 'Data Sharing', description: t('privacySettings.dataSharingDesc') || 'Allow sharing data for personalization', value: settings.dataSharing, onValueChange: value => updateSetting('dataSharing', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.locationSharing') || 'Location Sharing', description: t('privacySettings.locationSharingDesc') || 'Share your location', value: settings.locationSharing, onValueChange: value => updateSetting('locationSharing', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.analyticsSharing') || 'Analytics Sharing', description: t('privacySettings.analyticsSharingDesc') || 'Allow analytics data collection', value: settings.analyticsSharing, onValueChange: value => updateSetting('analyticsSharing', value) })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Section, { title: t('privacySettings.sections.content') || 'CONTENT & SAFETY', theme: theme, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.sensitiveContent') || 'Show Sensitive Content', description: t('privacySettings.sensitiveContentDesc') || 'Allow sensitive or explicit content', value: settings.sensitiveContent, onValueChange: value => updateSetting('sensitiveContent', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.autoFilter') || 'Auto Filter', description: t('privacySettings.autoFilterDesc') || 'Automatically filter inappropriate content', value: settings.autoFilter, onValueChange: value => updateSetting('autoFilter', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.muteKeywords') || 'Mute Keywords', description: t('privacySettings.muteKeywordsDesc') || 'Hide posts containing muted keywords', value: settings.muteKeywords, onValueChange: value => updateSetting('muteKeywords', value) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SettingRow, { title: t('privacySettings.blockScreenshots') || 'Block Screenshots', description: t('privacySettings.blockScreenshotsDesc') || 'Prevent screenshots of your content', value: settings.blockScreenshots, onValueChange: value => updateSetting('blockScreenshots', value) })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Section, { title: t('privacySettings.sections.blockedUsers') || 'BLOCKED USERS', theme: theme, children: isLoadingUsers ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.loadingUsersContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "small", color: themeStyles.textColor }) }) : blockedUsers.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.emptyContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.emptyText, { color: themeStyles.mutedTextColor }], children: t('privacySettings.noBlockedUsers') || 'No blocked users' }) }) : blockedUsers.map(blocked => { const { userId } = extractUserInfo(blocked, 'blockedId'); return /*#__PURE__*/(0, _jsxRuntime.jsx)(UserListItem, { item: blocked, idField: "blockedId", onAction: handleUnblock, actionLabel: t('privacySettings.unblock') || 'Unblock', actionColor: "#FF3B30" }, userId); }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Section, { title: t('privacySettings.sections.restrictedUsers') || 'RESTRICTED USERS', theme: theme, children: isLoadingUsers ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.loadingUsersContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "small", color: themeStyles.textColor }) }) : restrictedUsers.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.emptyContainer, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: [styles.emptyText, { color: themeStyles.mutedTextColor }], children: t('privacySettings.noRestrictedUsers') || 'No restricted users' }) }) : restrictedUsers.map(restricted => { const { userId } = extractUserInfo(restricted, 'restrictedId'); return /*#__PURE__*/(0, _jsxRuntime.jsx)(UserListItem, { item: restricted, idField: "restrictedId", onAction: handleUnrestrict, actionLabel: t('privacySettings.unrestrict') || 'Unrestrict', actionColor: "#007AFF", subtitle: t('privacySettings.restrictedDescription') || 'Limited interactions' }, userId); }) })] })] }); }; const styles = _reactNative.StyleSheet.create({ container: { flex: 1 }, content: { flex: 1, padding: 16 }, loadingContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' }, settingRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 16, borderBottomWidth: 1 }, settingInfo: { flex: 1, marginRight: 16 }, settingTitle: { fontSize: 16, fontWeight: '500', marginBottom: 4 }, settingDescription: { fontSize: 14, opacity: 0.7 }, loadingUsersContainer: { paddingVertical: 20, alignItems: 'center' }, emptyContainer: { paddingVertical: 20, alignItems: 'center' }, emptyText: { fontSize: 14 }, userRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1 }, userInfo: { flexDirection: 'row', alignItems: 'center', flex: 1, marginRight: 12 }, userDetails: { marginLeft: 12, flex: 1 }, username: { fontSize: 16, fontWeight: '500', marginBottom: 2 }, userSubtext: { fontSize: 13 }, actionButton: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 8 }, actionButtonText: { fontSize: 14, fontWeight: '600' } }); var _default = exports.default = /*#__PURE__*/_react.default.memo(PrivacySettingsScreen); //# sourceMappingURL=PrivacySettingsScreen.js.map