Settings Quick Start - nself-org/nchat GitHub Wiki
Quick reference for working with the settings pages backend integration.
// Profile settings
import { useProfileSettings } from '@/hooks/use-user-settings'
// Account settings
import { useAccountSettings } from '@/hooks/use-user-settings'
// Notification settings
import { useNotificationSettings } from '@/hooks/use-user-settings'
// Privacy settings
import { usePrivacySettings } from '@/hooks/use-user-settings'const { updateProfile, uploadAvatar, removeAvatar, updating, uploadingAvatar, removingAvatar } =
useProfileSettings()
// Update profile
await updateProfile({
displayName: 'John Doe',
bio: 'Developer',
timezone: 'America/New_York',
language: 'en',
})
// Upload avatar (automatically compressed)
await uploadAvatar(file)
// Remove avatar
await removeAvatar()const {
updateEmail,
updatePassword,
connectOAuth,
disconnectOAuth,
enableTwoFactor,
confirmTwoFactor,
disableTwoFactor,
deleteAccount,
} = useAccountSettings()
// Change email (requires password)
await updateEmail('[email protected]', 'currentPassword')
// Change password
await updatePassword('currentPassword', 'newPassword')
// Connect OAuth provider
await connectOAuth('google') // 'google' | 'github' | 'apple'
// Disconnect OAuth
await disconnectOAuth(accountId)
// Enable 2FA
const { secret, qrCodeData, backupCodes } = await enableTwoFactor()
// Show QR code to user, then:
await confirmTwoFactor(code, secret, backupCodes)
// Disable 2FA
await disableTwoFactor()
// Delete account
await deleteAccount()const { updateSettings, updating } = useNotificationSettings()
await updateSettings({
desktopEnabled: true,
desktopSound: true,
desktopPreview: true,
emailEnabled: true,
emailFrequency: 'daily',
emailDigest: true,
dndEnabled: false,
dndStart: '22:00',
dndEnd: '08:00',
dndWeekends: true,
directMessages: true,
mentions: true,
channelMessages: false,
threadReplies: true,
reactions: false,
})const { updateSettings, clearLocationHistory, updating, clearingLocation } = usePrivacySettings()
await updateSettings({
locationVisibility: 'contacts', // 'everyone' | 'contacts' | 'nobody'
useApproximateLocation: true,
defaultSharingDuration: 3600, // seconds
showNearbyPlaces: true,
saveLocationHistory: false,
locationHistoryRetentionDays: 30,
})
// Clear location history
await clearLocationHistory()import { validateImageFile, compressImage, createImagePreview } from '@/lib/image-utils'
// Validate image
const validation = validateImageFile(file)
if (!validation.valid) {
console.error(validation.error)
}
// Compress image
const compressed = await compressImage(file, {
maxWidth: 512,
maxHeight: 512,
quality: 0.85,
outputFormat: 'jpeg',
})
// Create preview
const previewUrl = await createImagePreview(file)All hooks include automatic error handling with toast notifications. Just wrap in try-catch if you need custom handling:
try {
await updateProfile(data)
// Custom success handling
} catch (error) {
// Custom error handling
// Toast already shown by hook
}All hooks return loading states:
const { updating, uploadingAvatar, removingAvatar } = useProfileSettings()
<Button disabled={updating || uploadingAvatar || removingAvatar}>
{updating ? 'Saving...' : 'Save'}
</Button>If you need to use mutations directly:
import { useMutation } from '@apollo/client'
import { UPDATE_USER_PROFILE } from '@/graphql/mutations/user-settings'
const [updateProfile] = useMutation(UPDATE_USER_PROFILE)
await updateProfile({
variables: {
userId: user.id,
displayName: 'John Doe',
bio: 'Developer',
},
})Available authentication endpoints:
// Verify password
POST /api/auth/verify-password
Body: { password: string }
// Change password
POST /api/auth/change-password
Body: { userId: string, currentPassword: string, newPassword: string }
// OAuth connect
GET /api/auth/oauth/connect?provider=google
// OAuth callback
GET /api/auth/oauth/callback?code=...&state=...
// 2FA setup
POST /api/auth/2fa/setup
Body: { userId: string }
// 2FA verify
POST /api/auth/2fa/verify
Body: { code: string, secret: string }Run the migration to add required columns:
cd .backend
nself db migrateOr manually run:
psql -U postgres -d your_database -f migrations/011_user_settings_columns.sqlRequired for production:
# OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GITHUB_CLIENT_ID=your_github_client_id
APPLE_CLIENT_ID=your_apple_client_id
# App URL for OAuth callbacks
NEXT_PUBLIC_APP_URL=https://yourdomain.com
# Storage
NEXT_PUBLIC_STORAGE_URL=https://storage.yourdomain.com
STORAGE_BUCKET=nchat-uploads-
Start the backend:
cd .backend nself start -
Start the frontend:
pnpm dev
-
Navigate to settings pages:
- Ensure storage service is running
- Check
NEXT_PUBLIC_STORAGE_URLis set - Verify file is a valid image format
- Check password is correct
- Ensure email is unique
- Verify database has
email_verifiedcolumn
- Ensure database has 2FA columns
- Check API routes are accessible
- Verify TOTP code is 6 digits
- Run database migration
- Check
nchat_user_locationstable exists - Verify user has permissions
// From src/graphql/mutations/user-settings.ts
interface UpdateProfileInput {
displayName?: string
bio?: string
timezone?: string
language?: string
}
interface NotificationSettings {
desktopEnabled: boolean
desktopSound: boolean
desktopPreview: boolean
emailEnabled: boolean
emailFrequency: 'instant' | 'daily' | 'weekly' | 'never'
emailDigest: boolean
dndEnabled: boolean
dndStart: string
dndEnd: string
dndWeekends: boolean
directMessages: boolean
mentions: boolean
channelMessages: boolean
threadReplies: boolean
reactions: boolean
}
interface LocationPrivacySettings {
locationVisibility: 'everyone' | 'contacts' | 'nobody'
useApproximateLocation: boolean
defaultSharingDuration: number
showNearbyPlaces: boolean
saveLocationHistory: boolean
locationHistoryRetentionDays: number
}- Always validate input before calling mutations
- Use loading states to prevent double submissions
- Show user feedback via toasts or inline messages
- Handle errors gracefully with user-friendly messages
- Clear sensitive data after operations (passwords, tokens)
- Use TypeScript types for type safety
- Test in dev mode before deploying to production
- Run migrations before testing new features
- Compress images before uploading (done automatically)
- Verify permissions for destructive operations
- Full documentation:
docs/Settings-Implementation.md - Implementation summary:
docs/Settings-Implementation-Summary.md - Check existing code in
src/app/settings/for examples - Review hooks in
src/hooks/use-user-settings.ts - Check mutations in
src/graphql/mutations/user-settings.ts