Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 0 additions & 49 deletions app/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
MoonIcon,
SunIcon,
ComputerDesktopIcon,
ExclamationTriangleIcon,
UserGroupIcon,
UserPlusIcon,
LockClosedIcon,
Expand All @@ -31,7 +30,6 @@ import * as RadioGroup from '@radix-ui/react-radio-group'
import { SettingsSwitch } from '@/components/settings/settings-switch'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import toast from 'react-hot-toast'
import { KeyBackupSettings } from '@/components/settings/key-backup-settings'
import { BlockedUsersSettings } from '@/components/settings/blocked-users'
import { PrivateFeedSettings } from '@/components/settings/private-feed-settings'
Expand Down Expand Up @@ -133,12 +131,6 @@ function SettingsPage() {
}
}

// Privacy settings
const [privacySettings, setPrivacySettings] = useState({
publicProfile: true,
showActivity: true,
})

// Account creation date from profile
const [accountCreatedAt, setAccountCreatedAt] = useState<Date | null>(null)

Expand Down Expand Up @@ -206,13 +198,6 @@ function SettingsPage() {
router.back()
}

// TODO: Implement account deletion
const handleDeleteAccount = () => {
if (confirm('Are you sure you want to delete your account? This action cannot be undone.')) {
toast.error('Account deletion is not yet implemented')
}
}

const renderMainSettings = () => (
<div className="divide-y divide-gray-200 dark:divide-gray-800">
{visibleSections.map((section) => (
Expand Down Expand Up @@ -313,14 +298,6 @@ function SettingsPage() {
<KeyIcon className="h-4 w-4 mr-2" />
Log Out
</Button>
<Button
variant="outline"
className="w-full justify-start text-red-600 hover:text-red-700 hover:border-red-300"
onClick={handleDeleteAccount}
>
<ExclamationTriangleIcon className="h-4 w-4 mr-2" />
Delete Account
</Button>
</div>
</div>
</div>
Expand Down Expand Up @@ -381,32 +358,6 @@ function SettingsPage() {
<div>
<h3 className="font-semibold mb-4">Privacy</h3>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Public Profile</p>
<p className="text-sm text-gray-500">Allow anyone to view your profile</p>
</div>
<SettingsSwitch
checked={privacySettings.publicProfile}
onCheckedChange={(checked) =>
setPrivacySettings(prev => ({ ...prev, publicProfile: checked }))
}
/>
</div>

<div className="flex items-center justify-between">
<div>
<p className="font-medium">Show Activity Status</p>
<p className="text-sm text-gray-500">Let others see when you&apos;re active</p>
</div>
<SettingsSwitch
checked={privacySettings.showActivity}
onCheckedChange={(checked) =>
setPrivacySettings(prev => ({ ...prev, showActivity: checked }))
}
/>
</div>

<div className="flex items-center justify-between">
<div>
<p className="font-medium">Link Previews</p>
Expand Down
9 changes: 7 additions & 2 deletions hooks/use-avatar.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { logger } from '@/lib/logger';
import { useState, useEffect, useCallback } from 'react'
import { useState, useEffect, useCallback, useRef } from 'react'
import type { DiceBearStyle } from '@/lib/services/unified-profile-service'

export interface AvatarSettings {
Expand Down Expand Up @@ -38,8 +38,11 @@ export function useAvatarSettings(userId: string): UseAvatarSettingsResult {
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [error, setError] = useState<string | null>(null)
// Bumped per load so a slow response for a previous user cannot overwrite a newer one.
const requestRef = useRef(0)

const loadSettings = useCallback(async () => {
const request = ++requestRef.current
if (!userId) {
setLoading(false)
return
Expand All @@ -53,6 +56,7 @@ export function useAvatarSettings(userId: string): UseAvatarSettingsResult {

// Get profile to extract avatar settings
const profile = await unifiedProfileService.getProfile(userId)
if (request !== requestRef.current) return

if (profile?.avatar) {
// Parse the avatar field to extract settings
Expand Down Expand Up @@ -119,10 +123,11 @@ export function useAvatarSettings(userId: string): UseAvatarSettingsResult {
})
}
} catch (err) {
if (request !== requestRef.current) return
logger.error('useAvatarSettings: Error loading settings:', err)
setError('Failed to load avatar settings')
} finally {
setLoading(false)
if (request === requestRef.current) setLoading(false)
}
}, [userId])

Expand Down
18 changes: 10 additions & 8 deletions hooks/use-crypto-price.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useState, useEffect, useCallback } from 'react'
import { useState, useEffect, useCallback, useRef } from 'react'
import { cryptoPriceService } from '@/lib/services/crypto-price-service'

export interface UseCryptoPriceResult {
Expand Down Expand Up @@ -30,14 +30,19 @@ export function useCryptoPrice(
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [fetchTrigger, setFetchTrigger] = useState(0)
const [skipCache, setSkipCache] = useState(false)
// A ref, not state: the effect reads it once per run and it must not itself re-run the effect.
const skipCacheRef = useRef(false)

const refetch = useCallback(() => {
setSkipCache(true)
skipCacheRef.current = true
setFetchTrigger((prev) => prev + 1)
}, [])

useEffect(() => {
// Read once per run; a refetch requested while inputs were invalid must not carry over.
const skipCache = skipCacheRef.current
skipCacheRef.current = false

// Reset state if inputs are invalid
if (!fiatAmount || !fiatCurrency || !scheme || fiatAmount <= 0) {
setCryptoAmount(null)
Expand Down Expand Up @@ -87,10 +92,7 @@ export function useCryptoPrice(
setPriceSources([])
setError(err instanceof Error ? err.message : 'Failed to fetch price')
} finally {
if (!cancelled) {
setIsLoading(false)
setSkipCache(false)
}
if (!cancelled) setIsLoading(false)
}
}

Expand All @@ -101,7 +103,7 @@ export function useCryptoPrice(
return () => {
cancelled = true
}
}, [fiatAmount, fiatCurrency, scheme, fetchTrigger, skipCache])
}, [fiatAmount, fiatCurrency, scheme, fetchTrigger])

return {
cryptoAmount,
Expand Down
19 changes: 10 additions & 9 deletions lib/services/direct-message-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ class DirectMessageService {
}
}

// 4. Check if we need to create a conversation invite
// 4. Create the conversation invite if this is the first message that way.
// An unknown answer (lookup failed) skips creation: a missing invite
// costs one inbox hint, a duplicate costs credits every time.
const existingInvite = await this.getMyInviteToRecipient(senderId, recipientId)

if (!existingInvite) {
if (existingInvite === null) {
// Create conversation invite
const senderPubKey = getPublicKeyFromPrivate(privateKey)

Expand Down Expand Up @@ -599,13 +601,11 @@ class DirectMessageService {
}
}

/**
* Get my invite to a recipient
*/
/** The sender's invite to the recipient; `null` when there is none, `undefined` when the lookup failed. */
private async getMyInviteToRecipient(
senderId: string,
recipientId: string
): Promise<Record<string, unknown> | null> {
): Promise<Record<string, unknown> | null | undefined> {
try {
const sdk = await getEvoSdk()

Expand All @@ -622,8 +622,9 @@ class DirectMessageService {

const docs = this.extractDocuments(response)
return docs[0] || null
} catch {
return null
} catch (error) {
logger.warn('Could not check for an existing conversation invite:', error)
return undefined
}
}

Expand All @@ -634,7 +635,7 @@ class DirectMessageService {
senderId: string,
recipientId: string
): Promise<Record<string, unknown> | null> {
return this.getMyInviteToRecipient(senderId, recipientId)
return (await this.getMyInviteToRecipient(senderId, recipientId)) ?? null
}

/**
Expand Down
Loading