diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3e5aef1..bc4161e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -506,9 +506,7 @@ export function App() { onChannelCreate: handleCreateCrackedChannel, }; const newMessageModalProps = { - contacts, undecryptedCount, - onSelectConversation: handleSelectConversationWithTargetReset, onCreateContact: handleCreateContact, onCreateChannel: handleCreateChannel, onCreateHashtagChannel: handleCreateHashtagChannel, diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index 9b50faf..84b6ed2 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -284,10 +284,6 @@ export function AppShell({ {...newMessageModalProps} open={showNewMessage} onClose={onCloseNewMessage} - onSelectConversation={(conv) => { - newMessageModalProps.onSelectConversation(conv); - onCloseNewMessage(); - }} /> diff --git a/frontend/src/components/NewMessageModal.tsx b/frontend/src/components/NewMessageModal.tsx index 6fc8d49..d44b282 100644 --- a/frontend/src/components/NewMessageModal.tsx +++ b/frontend/src/components/NewMessageModal.tsx @@ -1,7 +1,5 @@ import { useState, useRef } from 'react'; import { Dice5 } from 'lucide-react'; -import type { Contact, Conversation } from '../types'; -import { getContactDisplayName } from '../utils/pubkey'; import { Dialog, DialogContent, @@ -17,14 +15,12 @@ import { Checkbox } from './ui/checkbox'; import { Button } from './ui/button'; import { toast } from './ui/sonner'; -type Tab = 'existing' | 'new-contact' | 'new-room' | 'hashtag'; +type Tab = 'new-contact' | 'new-room' | 'hashtag'; interface NewMessageModalProps { open: boolean; - contacts: Contact[]; undecryptedCount: number; onClose: () => void; - onSelectConversation: (conversation: Conversation) => void; onCreateContact: (name: string, publicKey: string, tryHistorical: boolean) => Promise; onCreateChannel: (name: string, key: string, tryHistorical: boolean) => Promise; onCreateHashtagChannel: (name: string, tryHistorical: boolean) => Promise; @@ -32,15 +28,13 @@ interface NewMessageModalProps { export function NewMessageModal({ open, - contacts, undecryptedCount, onClose, - onSelectConversation, onCreateContact, onCreateChannel, onCreateHashtagChannel, }: NewMessageModalProps) { - const [tab, setTab] = useState('existing'); + const [tab, setTab] = useState('new-contact'); const [name, setName] = useState(''); const [contactKey, setContactKey] = useState(''); const [roomKey, setRoomKey] = useState(''); @@ -136,7 +130,7 @@ export function NewMessageModal({ } }; - const showHistoricalOption = tab !== 'existing' && undecryptedCount > 0; + const showHistoricalOption = undecryptedCount > 0; return ( New Conversation - {tab === 'existing' && 'Select an existing contact to start a conversation'} {tab === 'new-contact' && 'Add a new contact by entering their name and public key'} {tab === 'new-room' && 'Create a private room with a shared encryption key'} {tab === 'hashtag' && 'Join a public hashtag channel'} @@ -167,53 +160,12 @@ export function NewMessageModal({ }} className="w-full" > - - Existing + Contact - Room - Hashtag + Private Channel + Hashtag Channel - -
- {contacts.filter((contact) => contact.public_key.length === 64).length === 0 ? ( -
No contacts available
- ) : ( - contacts - .filter((contact) => contact.public_key.length === 64) - .map((contact) => ( -
{ - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - (e.currentTarget as HTMLElement).click(); - } - }} - onClick={() => { - onSelectConversation({ - type: 'contact', - id: contact.public_key, - name: getContactDisplayName( - contact.name, - contact.public_key, - contact.last_advert - ), - }); - resetForm(); - onClose(); - }} - > - {getContactDisplayName(contact.name, contact.public_key, contact.last_advert)} -
- )) - )} -
-
-
@@ -353,11 +305,9 @@ export function NewMessageModal({ {loading ? 'Creating...' : 'Create & Add Another'} )} - {tab !== 'existing' && ( - - )} +
diff --git a/frontend/src/test/newMessageModal.test.tsx b/frontend/src/test/newMessageModal.test.tsx index ad242cf..7dc013a 100644 --- a/frontend/src/test/newMessageModal.test.tsx +++ b/frontend/src/test/newMessageModal.test.tsx @@ -10,7 +10,6 @@ import userEvent from '@testing-library/user-event'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { NewMessageModal } from '../components/NewMessageModal'; -import type { Contact } from '../types'; import { toast } from '../components/ui/sonner'; // Mock sonner (toast) @@ -18,24 +17,6 @@ vi.mock('../components/ui/sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() }, })); -const mockContact: Contact = { - public_key: 'aa'.repeat(32), - name: 'Alice', - type: 1, - flags: 0, - direct_path: null, - direct_path_len: -1, - direct_path_hash_mode: 0, - last_advert: null, - lat: null, - lon: null, - last_seen: null, - on_radio: false, - last_contacted: null, - last_read_at: null, - first_seen: null, -}; - const mockToast = toast as unknown as { success: ReturnType; error: ReturnType; @@ -43,7 +24,6 @@ const mockToast = toast as unknown as { describe('NewMessageModal form reset', () => { const onClose = vi.fn(); - const onSelectConversation = vi.fn(); const onCreateContact = vi.fn().mockResolvedValue(undefined); const onCreateChannel = vi.fn().mockResolvedValue(undefined); const onCreateHashtagChannel = vi.fn().mockResolvedValue(undefined); @@ -56,10 +36,8 @@ describe('NewMessageModal form reset', () => { return render( { it('clears name after successful Create', async () => { const user = userEvent.setup(); const { unmount } = renderModal(); - await switchToTab(user, 'Hashtag'); + await switchToTab(user, 'Hashtag Channel'); const input = screen.getByPlaceholderText('channel-name') as HTMLInputElement; await user.type(input, 'testchan'); @@ -91,14 +69,14 @@ describe('NewMessageModal form reset', () => { // Re-render to simulate reopening — state should be reset renderModal(); - await switchToTab(user, 'Hashtag'); + await switchToTab(user, 'Hashtag Channel'); expect((screen.getByPlaceholderText('channel-name') as HTMLInputElement).value).toBe(''); }); it('clears name when Cancel is clicked', async () => { const user = userEvent.setup(); renderModal(); - await switchToTab(user, 'Hashtag'); + await switchToTab(user, 'Hashtag Channel'); const input = screen.getByPlaceholderText('channel-name') as HTMLInputElement; await user.type(input, 'mychannel'); @@ -131,7 +109,7 @@ describe('NewMessageModal form reset', () => { it('clears name and key after successful Create', async () => { const user = userEvent.setup(); renderModal(); - await switchToTab(user, 'Room'); + await switchToTab(user, 'Private Channel'); await user.type(screen.getByPlaceholderText('Room name'), 'MyRoom'); await user.type(screen.getByPlaceholderText('Pre-shared key (hex)'), 'cc'.repeat(16)); @@ -148,7 +126,7 @@ describe('NewMessageModal form reset', () => { const user = userEvent.setup(); onCreateChannel.mockRejectedValueOnce(new Error('Bad key')); renderModal(); - await switchToTab(user, 'Room'); + await switchToTab(user, 'Private Channel'); await user.type(screen.getByPlaceholderText('Room name'), 'MyRoom'); await user.type(screen.getByPlaceholderText('Pre-shared key (hex)'), 'cc'.repeat(16)); @@ -172,8 +150,8 @@ describe('NewMessageModal form reset', () => { await user.type(screen.getByPlaceholderText('Contact name'), 'Bob'); await user.type(screen.getByPlaceholderText('64-character hex public key'), 'deadbeef'); - // Switch to Room tab — fields should reset - await switchToTab(user, 'Room'); + // Switch to Private Channel tab — fields should reset + await switchToTab(user, 'Private Channel'); expect((screen.getByPlaceholderText('Room name') as HTMLInputElement).value).toBe(''); expect((screen.getByPlaceholderText('Pre-shared key (hex)') as HTMLInputElement).value).toBe( @@ -184,12 +162,12 @@ describe('NewMessageModal form reset', () => { it('clears room fields when switching to hashtag tab', async () => { const user = userEvent.setup(); renderModal(); - await switchToTab(user, 'Room'); + await switchToTab(user, 'Private Channel'); await user.type(screen.getByPlaceholderText('Room name'), 'SecretRoom'); await user.type(screen.getByPlaceholderText('Pre-shared key (hex)'), 'ff'.repeat(16)); - await switchToTab(user, 'Hashtag'); + await switchToTab(user, 'Hashtag Channel'); expect((screen.getByPlaceholderText('channel-name') as HTMLInputElement).value).toBe(''); }); @@ -199,7 +177,7 @@ describe('NewMessageModal form reset', () => { it('resets tryHistorical when switching tabs', async () => { const user = userEvent.setup(); renderModal(); - await switchToTab(user, 'Hashtag'); + await switchToTab(user, 'Hashtag Channel'); // Check the "Try decrypting" checkbox const checkbox = screen.getByRole('checkbox', { name: /Try decrypting/ }); @@ -210,7 +188,7 @@ describe('NewMessageModal form reset', () => { // Switch tab and come back await switchToTab(user, 'Contact'); - await switchToTab(user, 'Hashtag'); + await switchToTab(user, 'Hashtag Channel'); // The streaming message should be gone (tryHistorical was reset) expect(screen.queryByText(/Messages will stream in/)).toBeNull();