@oxyhq/services
Version:
469 lines (462 loc) • 19.1 kB
JavaScript
"use strict";
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
import { toast } from '../../lib/sonner';
import { Header, Section, Avatar, SettingRow, LoadingState, EmptyState, GroupedSection } from "../components/index.js";
import { useI18n } from "../hooks/useI18n.js";
import { useThemeStyles } from "../hooks/useThemeStyles.js";
import { useSettingToggles } from "../hooks/useSettingToggle.js";
import { normalizeTheme } from "../utils/themeUtils.js";
import { useOxy } from "../context/OxyContext.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const DEFAULT_PRIVACY_SETTINGS = {
isPrivateAccount: false,
hideOnlineStatus: false,
hideLastSeen: false,
profileVisibility: true,
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 PrivacySettingsScreen = ({
onClose,
theme,
goBack
}) => {
const {
oxyServices,
user
} = useOxy();
const {
t
} = useI18n();
const [isLoading, setIsLoading] = useState(true);
const [blockedUsers, setBlockedUsers] = useState([]);
const [restrictedUsers, setRestrictedUsers] = useState([]);
const [isLoadingUsers, setIsLoadingUsers] = useState(false);
// Use the existing useSettingToggles hook for toggle management
const {
values: settings,
toggle,
savingKeys,
setValues
} = useSettingToggles({
initialValues: DEFAULT_PRIVACY_SETTINGS,
onSave: async (key, value) => {
if (!user?.id || !oxyServices) return;
await oxyServices.updatePrivacySettings({
[key]: value
}, user.id);
},
errorMessage: t('privacySettings.updateError') || 'Failed to update privacy setting'
});
const isSaving = savingKeys.size > 0;
// Load settings
useEffect(() => {
const loadSettings = async () => {
try {
setIsLoading(true);
if (user?.id && oxyServices) {
const privacySettings = await oxyServices.getPrivacySettings(user.id);
if (privacySettings) {
setValues(privacySettings);
}
}
} catch (error) {
if (__DEV__) {
console.error('Failed to load privacy settings:', error);
}
toast.error(t('privacySettings.loadError') || 'Failed to load privacy settings');
} finally {
setIsLoading(false);
}
};
loadSettings();
}, [user?.id, oxyServices, t, setValues]);
// Load blocked and restricted users
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) {
if (__DEV__) {
console.error('Failed to load blocked/restricted users:', error);
}
} finally {
setIsLoadingUsers(false);
}
};
loadUsers();
}, [oxyServices]);
const handleUnblock = 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;
}));
toast.success(t('privacySettings.userUnblocked') || 'User unblocked');
} catch (error) {
if (__DEV__) {
console.error('Failed to unblock user:', error);
}
toast.error(t('privacySettings.unblockError') || 'Failed to unblock user');
}
}, [oxyServices, t]);
const handleUnrestrict = 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;
}));
toast.success(t('privacySettings.userUnrestricted') || 'User unrestricted');
} catch (error) {
if (__DEV__) {
console.error('Failed to unrestrict user:', error);
}
toast.error(t('privacySettings.unrestrictError') || 'Failed to unrestrict user');
}
}, [oxyServices, t]);
// Helper to extract user info from blocked/restricted objects
const extractUserInfo = 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
};
}, []);
const normalizedTheme = normalizeTheme(theme);
const themeStyles = useThemeStyles(normalizedTheme);
// Convert blocked users to GroupedSection items
const blockedUserItems = useMemo(() => {
return blockedUsers.map(blocked => {
const {
userId,
username,
avatar
} = extractUserInfo(blocked, 'blockedId');
const avatarUri = avatar && oxyServices ? oxyServices.getFileDownloadUrl(avatar, 'thumb') : undefined;
return {
id: userId,
title: username,
customIcon: /*#__PURE__*/_jsx(Avatar, {
uri: avatarUri,
name: username,
size: 40
}),
customContent: /*#__PURE__*/_jsx(TouchableOpacity, {
onPress: () => handleUnblock(userId),
style: [styles.actionButton, {
backgroundColor: themeStyles.secondaryBackgroundColor
}],
children: /*#__PURE__*/_jsx(Text, {
style: [styles.actionButtonText, {
color: themeStyles.dangerColor
}],
children: t('privacySettings.unblock') || 'Unblock'
})
})
};
});
}, [blockedUsers, oxyServices, themeStyles, handleUnblock, t]);
// Convert restricted users to GroupedSection items
const restrictedUserItems = useMemo(() => {
return restrictedUsers.map(restricted => {
const {
userId,
username,
avatar
} = extractUserInfo(restricted, 'restrictedId');
const avatarUri = avatar && oxyServices ? oxyServices.getFileDownloadUrl(avatar, 'thumb') : undefined;
return {
id: userId,
title: username,
subtitle: t('privacySettings.restrictedDescription') || 'Limited interactions',
customIcon: /*#__PURE__*/_jsx(Avatar, {
uri: avatarUri,
name: username,
size: 40
}),
customContent: /*#__PURE__*/_jsx(TouchableOpacity, {
onPress: () => handleUnrestrict(userId),
style: [styles.actionButton, {
backgroundColor: themeStyles.secondaryBackgroundColor
}],
children: /*#__PURE__*/_jsx(Text, {
style: [styles.actionButtonText, {
color: themeStyles.primaryColor
}],
children: t('privacySettings.unrestrict') || 'Unrestrict'
})
})
};
});
}, [restrictedUsers, oxyServices, themeStyles, handleUnrestrict, t]);
if (isLoading) {
return /*#__PURE__*/_jsxs(View, {
style: [styles.container, {
backgroundColor: themeStyles.backgroundColor
}],
children: [/*#__PURE__*/_jsx(Header, {
title: t('privacySettings.title') || 'Privacy Settings',
onBack: goBack || onClose,
variant: "minimal",
elevation: "subtle"
}), /*#__PURE__*/_jsx(LoadingState, {
color: themeStyles.textColor
})]
});
}
return /*#__PURE__*/_jsxs(View, {
style: [styles.container, {
backgroundColor: themeStyles.backgroundColor
}],
children: [/*#__PURE__*/_jsx(Header, {
title: t('privacySettings.title') || 'Privacy Settings',
onBack: goBack || onClose,
variant: "minimal",
elevation: "subtle"
}), /*#__PURE__*/_jsxs(ScrollView, {
style: styles.content,
children: [/*#__PURE__*/_jsxs(Section, {
title: t('privacySettings.sections.account') || 'ACCOUNT PRIVACY',
isFirst: true,
children: [/*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.isPrivateAccount') || 'Private Account',
description: t('privacySettings.isPrivateAccountDesc') || 'Only approved followers can see your posts',
value: settings.isPrivateAccount,
onValueChange: () => toggle('isPrivateAccount'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.profileVisibility') || 'Profile Visibility',
description: t('privacySettings.profileVisibilityDesc') || 'Control who can view your profile',
value: settings.profileVisibility,
onValueChange: () => toggle('profileVisibility'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.hideOnlineStatus') || 'Hide Online Status',
description: t('privacySettings.hideOnlineStatusDesc') || 'Don\'t show when you\'re online',
value: settings.hideOnlineStatus,
onValueChange: () => toggle('hideOnlineStatus'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_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: () => toggle('hideLastSeen'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
})]
}), /*#__PURE__*/_jsxs(Section, {
title: t('privacySettings.sections.interactions') || 'INTERACTIONS',
children: [/*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.allowTagging') || 'Allow Tagging',
description: t('privacySettings.allowTaggingDesc') || 'Let others tag you in posts',
value: settings.allowTagging,
onValueChange: () => toggle('allowTagging'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.allowMentions') || 'Allow Mentions',
description: t('privacySettings.allowMentionsDesc') || 'Let others mention you',
value: settings.allowMentions,
onValueChange: () => toggle('allowMentions'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.allowDirectMessages') || 'Allow Direct Messages',
description: t('privacySettings.allowDirectMessagesDesc') || 'Let others send you direct messages',
value: settings.allowDirectMessages,
onValueChange: () => toggle('allowDirectMessages'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.hideReadReceipts') || 'Hide Read Receipts',
description: t('privacySettings.hideReadReceiptsDesc') || 'Don\'t show read receipts in messages',
value: settings.hideReadReceipts,
onValueChange: () => toggle('hideReadReceipts'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
})]
}), /*#__PURE__*/_jsxs(Section, {
title: t('privacySettings.sections.activity') || 'ACTIVITY & DATA',
children: [/*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.showActivity') || 'Show Activity Status',
description: t('privacySettings.showActivityDesc') || 'Display your activity on your profile',
value: settings.showActivity,
onValueChange: () => toggle('showActivity'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.dataSharing') || 'Data Sharing',
description: t('privacySettings.dataSharingDesc') || 'Allow sharing data for personalization',
value: settings.dataSharing,
onValueChange: () => toggle('dataSharing'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.locationSharing') || 'Location Sharing',
description: t('privacySettings.locationSharingDesc') || 'Share your location',
value: settings.locationSharing,
onValueChange: () => toggle('locationSharing'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.analyticsSharing') || 'Analytics Sharing',
description: t('privacySettings.analyticsSharingDesc') || 'Allow analytics data collection',
value: settings.analyticsSharing,
onValueChange: () => toggle('analyticsSharing'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
})]
}), /*#__PURE__*/_jsxs(Section, {
title: t('privacySettings.sections.content') || 'CONTENT & SAFETY',
children: [/*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.sensitiveContent') || 'Show Sensitive Content',
description: t('privacySettings.sensitiveContentDesc') || 'Allow sensitive or explicit content',
value: settings.sensitiveContent,
onValueChange: () => toggle('sensitiveContent'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.autoFilter') || 'Auto Filter',
description: t('privacySettings.autoFilterDesc') || 'Automatically filter inappropriate content',
value: settings.autoFilter,
onValueChange: () => toggle('autoFilter'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.muteKeywords') || 'Mute Keywords',
description: t('privacySettings.muteKeywordsDesc') || 'Hide posts containing muted keywords',
value: settings.muteKeywords,
onValueChange: () => toggle('muteKeywords'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
}), /*#__PURE__*/_jsx(SettingRow, {
title: t('privacySettings.blockScreenshots') || 'Block Screenshots',
description: t('privacySettings.blockScreenshotsDesc') || 'Prevent screenshots of your content',
value: settings.blockScreenshots,
onValueChange: () => toggle('blockScreenshots'),
disabled: isSaving,
textColor: themeStyles.textColor,
mutedTextColor: themeStyles.mutedTextColor,
borderColor: themeStyles.borderColor
})]
}), /*#__PURE__*/_jsx(Section, {
title: t('privacySettings.sections.blockedUsers') || 'BLOCKED USERS',
children: isLoadingUsers ? /*#__PURE__*/_jsx(LoadingState, {
color: themeStyles.textColor,
size: "small"
}) : blockedUsers.length === 0 ? /*#__PURE__*/_jsx(EmptyState, {
message: t('privacySettings.noBlockedUsers') || 'No blocked users',
textColor: themeStyles.mutedTextColor
}) : /*#__PURE__*/_jsx(GroupedSection, {
items: blockedUserItems
})
}), /*#__PURE__*/_jsx(Section, {
title: t('privacySettings.sections.restrictedUsers') || 'RESTRICTED USERS',
children: isLoadingUsers ? /*#__PURE__*/_jsx(LoadingState, {
color: themeStyles.textColor,
size: "small"
}) : restrictedUsers.length === 0 ? /*#__PURE__*/_jsx(EmptyState, {
message: t('privacySettings.noRestrictedUsers') || 'No restricted users',
textColor: themeStyles.mutedTextColor
}) : /*#__PURE__*/_jsx(GroupedSection, {
items: restrictedUserItems
})
})]
})]
});
};
const styles = StyleSheet.create({
container: {
flex: 1
},
content: {
flex: 1,
padding: 16
},
actionButton: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 8
},
actionButtonText: {
fontSize: 14,
fontWeight: '600'
}
});
export default /*#__PURE__*/React.memo(PrivacySettingsScreen);
//# sourceMappingURL=PrivacySettingsScreen.js.map