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