mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
Add profile appearance and notifications settings
Co-authored-by: Claude Code <claude@anthropic.com>
This commit is contained in:
1 parent
b082da436b
commit
1452519c76
7 files changed
+115
-6
No files matched your search
@@ -142,9 +142,10 @@ Track progress by checking off completed items.
|
||||
- [x] Extract `ProfileHeader.tsx`
|
||||
- [x] Extract `CoupleStatus.tsx`
|
||||
- [x] Extract `SettingsSection.tsx`
|
||||
- [ ] Extract `NotificationSettings.tsx`
|
||||
- [x] Extract `PrivacySettings.tsx`
|
||||
- [ ] Extract `AppearanceSettings.tsx`
|
||||
- [x] Extract `NotificationSettings.tsx`
|
||||
- [x] Extract `PrivacySettings.tsx`
|
||||
- [x] Extract `AppearanceSettings.tsx`
|
||||
|
||||
- [x] Extract `DangerZone.tsx`
|
||||
- [x] Extract `SwitchItem.tsx`
|
||||
- [x] Extract `MenuItem.tsx`
|
||||
|
||||
@@ -17,7 +17,7 @@ import { supabase } from '../../lib/supabase'; // Needed for debug reset
|
||||
import { useProfileSettings, useCoupleManagement } from './hooks';
|
||||
|
||||
// Components
|
||||
import { ProfileHeader, CoupleStatus, PrivacySettings, DangerZone, SettingsSection, MenuItem } from './components';
|
||||
import { AppearanceSettings, CoupleStatus, NotificationSettings, PrivacySettings, DangerZone, SettingsSection, MenuItem } from './components';
|
||||
|
||||
const MAX_CONTENT_WIDTH = 500;
|
||||
const ACCENT_GRADIENT = featureColors.profile.gradient as [string, string];
|
||||
@@ -81,7 +81,7 @@ export function ProfileScreen() {
|
||||
</Animated.View>
|
||||
|
||||
{/* Profile Header */}
|
||||
<ProfileHeader
|
||||
<AppearanceSettings
|
||||
user={user}
|
||||
isUploadingAvatar={settings.isUploadingAvatar}
|
||||
isEditingName={settings.isEditingName}
|
||||
@@ -175,6 +175,13 @@ export function ProfileScreen() {
|
||||
)}
|
||||
</SettingsSection>
|
||||
|
||||
{/* Notifications */}
|
||||
<NotificationSettings
|
||||
pushEnabled={settings.pushEnabled}
|
||||
isUpdatingPush={settings.isUpdatingPush}
|
||||
onPushToggle={settings.handlePushToggle}
|
||||
/>
|
||||
|
||||
{/* Preferences */}
|
||||
<PrivacySettings
|
||||
showExplicit={settings.showExplicit}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
import { ProfileHeader } from './ProfileHeader';
|
||||
import type { ProfileHeaderProps } from './ProfileHeader';
|
||||
|
||||
export type AppearanceSettingsProps = ProfileHeaderProps;
|
||||
|
||||
export const AppearanceSettings: React.FC<AppearanceSettingsProps> = (props) => {
|
||||
return <ProfileHeader {...props} />;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SettingsSection } from './SettingsSection';
|
||||
import { SwitchItem } from './SwitchItem';
|
||||
|
||||
export interface NotificationSettingsProps {
|
||||
pushEnabled: boolean;
|
||||
isUpdatingPush: boolean;
|
||||
onPushToggle: (value: boolean) => void;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export const NotificationSettings: React.FC<NotificationSettingsProps> = ({
|
||||
pushEnabled,
|
||||
isUpdatingPush,
|
||||
onPushToggle,
|
||||
delay = 360,
|
||||
}) => {
|
||||
return (
|
||||
<SettingsSection title="Notifications" delay={delay}>
|
||||
<SwitchItem
|
||||
icon="notifications-outline"
|
||||
label="Push Notifications"
|
||||
description="Get alerts for matches and messages"
|
||||
value={pushEnabled}
|
||||
onValueChange={onPushToggle}
|
||||
disabled={isUpdatingPush}
|
||||
/>
|
||||
</SettingsSection>
|
||||
);
|
||||
};
|
||||
@@ -10,7 +10,7 @@ import { Profile } from '../../../types';
|
||||
|
||||
const ACCENT_GRADIENT = featureColors.profile.gradient as [string, string];
|
||||
|
||||
interface ProfileHeaderProps {
|
||||
export interface ProfileHeaderProps {
|
||||
user: Profile | null;
|
||||
isUploadingAvatar: boolean;
|
||||
isEditingName: boolean;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
// Profile feature components
|
||||
export { ProfileHeader } from './ProfileHeader';
|
||||
export type { ProfileHeaderProps } from './ProfileHeader';
|
||||
export { AppearanceSettings } from './AppearanceSettings';
|
||||
export type { AppearanceSettingsProps } from './AppearanceSettings';
|
||||
export { NotificationSettings } from './NotificationSettings';
|
||||
export type { NotificationSettingsProps } from './NotificationSettings';
|
||||
export { CoupleStatus } from './CoupleStatus';
|
||||
export { PrivacySettings } from './PrivacySettings';
|
||||
export { DangerZone } from './DangerZone';
|
||||
|
||||
@@ -6,6 +6,11 @@ import { decode } from 'base64-arraybuffer';
|
||||
import { useAuthStore, useSubscriptionStore, usePacksStore } from '../../../store';
|
||||
import { supabase } from '../../../lib/supabase';
|
||||
import { Events } from '../../../lib/analytics';
|
||||
import {
|
||||
registerForPushNotificationsAsync,
|
||||
savePushToken,
|
||||
clearPushToken,
|
||||
} from '../../../lib/notifications';
|
||||
import {
|
||||
isBiometricAvailable,
|
||||
isBiometricEnabled,
|
||||
@@ -30,6 +35,10 @@ export function useProfileSettings() {
|
||||
const [showExplicit, setShowExplicit] = useState(user?.show_explicit_content ?? true);
|
||||
const [isUpdatingExplicit, setIsUpdatingExplicit] = useState(false);
|
||||
|
||||
// Notifications
|
||||
const [pushEnabled, setPushEnabled] = useState(!!user?.push_token);
|
||||
const [isUpdatingPush, setIsUpdatingPush] = useState(false);
|
||||
|
||||
// Biometrics
|
||||
const [biometricAvailable, setBiometricAvailable] = useState(false);
|
||||
const [biometricEnabledState, setBiometricEnabledState] = useState(false);
|
||||
@@ -47,6 +56,7 @@ export function useProfileSettings() {
|
||||
if (user?.show_explicit_content !== undefined) {
|
||||
setShowExplicit(user.show_explicit_content);
|
||||
}
|
||||
setPushEnabled(!!user?.push_token);
|
||||
}, [user]);
|
||||
|
||||
// Check biometric availability
|
||||
@@ -276,6 +286,48 @@ export function useProfileSettings() {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePushToggle = async (value: boolean) => {
|
||||
if (!user?.id) return;
|
||||
|
||||
if (Platform.OS === 'web') {
|
||||
setPushEnabled(false);
|
||||
Alert.alert("Not Supported", "Push notifications aren't available on web.");
|
||||
return;
|
||||
}
|
||||
|
||||
setPushEnabled(value);
|
||||
setIsUpdatingPush(true);
|
||||
|
||||
try {
|
||||
if (value) {
|
||||
const token = await registerForPushNotificationsAsync();
|
||||
if (!token) {
|
||||
setPushEnabled(false);
|
||||
Alert.alert(
|
||||
"Enable Notifications",
|
||||
"Allow notifications in system settings to receive alerts.",
|
||||
[
|
||||
{ text: "Not Now", style: "cancel" },
|
||||
{ text: "Open Settings", onPress: () => Linking.openSettings() },
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await savePushToken(user.id, token);
|
||||
} else {
|
||||
await clearPushToken(user.id);
|
||||
}
|
||||
|
||||
await fetchUser();
|
||||
} catch (error) {
|
||||
setPushEnabled(!value);
|
||||
Alert.alert("Error", "Failed to update notification settings. Please try again.");
|
||||
} finally {
|
||||
setIsUpdatingPush(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBiometricToggle = async (value: boolean) => {
|
||||
setIsUpdatingBiometric(true);
|
||||
|
||||
@@ -330,6 +382,8 @@ export function useProfileSettings() {
|
||||
isUploadingAvatar,
|
||||
showExplicit,
|
||||
isUpdatingExplicit,
|
||||
pushEnabled,
|
||||
isUpdatingPush,
|
||||
biometricAvailable,
|
||||
biometricEnabled: biometricEnabledState,
|
||||
biometricType,
|
||||
@@ -343,6 +397,7 @@ export function useProfileSettings() {
|
||||
handleCancelEditName,
|
||||
handleAvatarPress,
|
||||
handleExplicitToggle,
|
||||
handlePushToggle,
|
||||
handleBiometricToggle,
|
||||
handleRestorePurchases,
|
||||
handleManageSubscription,
|
||||
|
||||
Reference in new issue
Block a user