Add dropdown to choose contact addition type. Closes #216.

This commit is contained in:
Jack Kingsman
2026-04-22 17:42:27 -07:00
parent 091ba06ccf
commit 55f05bf03b
6 changed files with 31 additions and 7 deletions

View File

@@ -221,6 +221,9 @@ class CreateContactRequest(BaseModel):
public_key: str = Field(min_length=64, max_length=64, description="Public key (64-char hex)")
name: str | None = Field(default=None, description="Display name for the contact")
type: int = Field(
default=0, ge=0, le=3, description="Contact type (0=unknown, 1=client, 2=repeater, 3=room)"
)
try_historical: bool = Field(
default=False,
description="Attempt to decrypt historical DM packets for this contact",

View File

@@ -315,6 +315,7 @@ async def create_contact(
contact_upsert = ContactUpsert(
public_key=lower_key,
name=request.name,
type=request.type,
on_radio=False,
)
await ContactRepository.upsert(contact_upsert)

View File

@@ -158,10 +158,10 @@ export const api = {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ public_keys: publicKeys }),
}),
createContact: (publicKey: string, name?: string, tryHistorical?: boolean) =>
createContact: (publicKey: string, name?: string, tryHistorical?: boolean, type?: number) =>
fetchJson<Contact>('/contacts', {
method: 'POST',
body: JSON.stringify({ public_key: publicKey, name, try_historical: tryHistorical }),
body: JSON.stringify({ public_key: publicKey, name, type, try_historical: tryHistorical }),
}),
markContactRead: (publicKey: string) =>
fetchJson<{ status: string; public_key: string }>(`/contacts/${publicKey}/mark-read`, {

View File

@@ -32,7 +32,12 @@ interface NewMessageModalProps {
nonce: number;
} | null;
onClose: () => void;
onCreateContact: (name: string, publicKey: string, tryHistorical: boolean) => Promise<void>;
onCreateContact: (
name: string,
publicKey: string,
tryHistorical: boolean,
type?: number
) => Promise<void>;
onCreateChannel: (name: string, key: string, tryHistorical: boolean) => Promise<void>;
onCreateHashtagChannel: (name: string, tryHistorical: boolean) => Promise<void>;
onBulkAddHashtagChannels: (channelNames: string[], tryHistorical: boolean) => Promise<void>;
@@ -91,6 +96,7 @@ export function NewMessageModal({
}: NewMessageModalProps) {
const [tab, setTab] = useState<Tab>('new-contact');
const [name, setName] = useState('');
const [contactType, setContactType] = useState(1);
const [contactKey, setContactKey] = useState('');
const [channelKey, setChannelKey] = useState('');
const [bulkChannelText, setBulkChannelText] = useState('');
@@ -103,6 +109,7 @@ export function NewMessageModal({
const resetForm = () => {
setName('');
setContactType(1);
setContactKey('');
setChannelKey('');
setBulkChannelText('');
@@ -161,7 +168,7 @@ export function NewMessageModal({
setError('Name and public key are required');
return;
}
await onCreateContact(name.trim(), contactKey.trim(), tryHistorical);
await onCreateContact(name.trim(), contactKey.trim(), tryHistorical, contactType);
} else if (tab === 'new-channel') {
if (!name.trim() || !channelKey.trim()) {
setError('Channel name and key are required');
@@ -293,6 +300,19 @@ export function NewMessageModal({
placeholder="64-character hex public key"
/>
</div>
<div className="space-y-2">
<Label htmlFor="contact-type">Type</Label>
<select
id="contact-type"
value={contactType}
onChange={(e) => setContactType(Number(e.target.value))}
className="block h-9 w-full rounded-md border border-input bg-background px-3 text-sm shadow-sm"
>
<option value={1}>Client</option>
<option value={2}>Repeater</option>
<option value={3}>Room Server</option>
</select>
</div>
</TabsContent>
<TabsContent value="new-channel" className="mt-4 space-y-4">

View File

@@ -50,8 +50,8 @@ export function useContactsAndChannels({
}, []);
const handleCreateContact = useCallback(
async (name: string, publicKey: string, tryHistorical: boolean) => {
const created = await api.createContact(publicKey, name || undefined, tryHistorical);
async (name: string, publicKey: string, tryHistorical: boolean, type?: number) => {
const created = await api.createContact(publicKey, name || undefined, tryHistorical, type);
const data = await fetchAllContacts();
setContacts(data);

View File

@@ -172,7 +172,7 @@ describe('NewMessageModal form reset', () => {
await user.click(screen.getByRole('button', { name: 'Create' }));
await waitFor(() => {
expect(onCreateContact).toHaveBeenCalledWith('Bob', 'bb'.repeat(32), false);
expect(onCreateContact).toHaveBeenCalledWith('Bob', 'bb'.repeat(32), false, 1);
});
expect(onClose).toHaveBeenCalled();
});