refactor(web): FriendsPage — toast on server errors, drop ConnectInstanceModal triggers
Removes try/catch on the deleted InstanceNotConnectedError/Disconnected classes (T17). Server now returns structured error codes; client maps them to human-readable toasts via the new mapServerErrorToMessage helper. The friend-add flow no longer triggers ConnectInstanceModal — the server handles all routing/peering/lookup. The modal itself stays for Connections settings and space-join flows.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSocialStore, type TaggedFriend, type TaggedFriendRequest, type TaggedUser, InstanceNotConnectedError, InstanceDisconnectedError } from '../../stores/socialStore';
|
||||
import { useSocialStore, type TaggedFriend, type TaggedFriendRequest, type TaggedUser } from '../../stores/socialStore';
|
||||
import { useAuthStore } from '../../stores/authStore';
|
||||
import { ConnectInstanceModal } from '../modals/ConnectInstanceModal';
|
||||
import { useDiscoverStore, type TaggedDiscoverUser } from '../../stores/discoverStore';
|
||||
import { mapServerErrorToMessage } from '../../utils/friendErrors';
|
||||
import { useSpaceStore } from '../../stores/spaceStore';
|
||||
import { useInstanceStore } from '../../stores/instanceStore';
|
||||
import { useUIStore } from '../../stores/uiStore';
|
||||
@@ -398,11 +398,6 @@ function AddFriendTab({
|
||||
const [rawSearchResults, setRawSearchResults] = useState<TaggedUser[]>([]);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const [directAddLoading, setDirectAddLoading] = useState(false);
|
||||
const [connectModal, setConnectModal] = useState<{
|
||||
domain: string;
|
||||
isReconnect: boolean;
|
||||
username: string;
|
||||
} | null>(null);
|
||||
|
||||
// Fetch discover on mount
|
||||
useEffect(() => {
|
||||
@@ -481,34 +476,21 @@ function AddFriendTab({
|
||||
addToast('Friend request sent!', 'success');
|
||||
setQuery('');
|
||||
} catch (err) {
|
||||
if (err instanceof InstanceNotConnectedError) {
|
||||
setConnectModal({ domain: err.domain, isReconnect: false, username: query.trim() });
|
||||
} else if (err instanceof InstanceDisconnectedError) {
|
||||
setConnectModal({ domain: err.domain, isReconnect: true, username: query.trim() });
|
||||
} else {
|
||||
addToast((err as Error).message, 'warning');
|
||||
const errorBody = (err as { body?: unknown })?.body;
|
||||
let code: string | undefined;
|
||||
let message: string | undefined;
|
||||
if (errorBody && typeof errorBody === 'object') {
|
||||
code = (errorBody as { error?: string }).error;
|
||||
message = (errorBody as { message?: string }).message;
|
||||
}
|
||||
// Fall back to the Error message if the server didn't send a structured body.
|
||||
const fallback = message ?? (err instanceof Error ? err.message : undefined);
|
||||
addToast(mapServerErrorToMessage(code, fallback, query.trim()), 'warning');
|
||||
} finally {
|
||||
setDirectAddLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Connect modal handler
|
||||
const handleConnected = async (result: 'new' | 'reconnect') => {
|
||||
const username = connectModal?.username;
|
||||
const domain = connectModal?.domain;
|
||||
setConnectModal(null);
|
||||
if (!username) return;
|
||||
try {
|
||||
await sendFriendRequest(username);
|
||||
const verb = result === 'reconnect' ? 'Reconnected to' : 'Connected to';
|
||||
addToast(`${verb} ${domain} — friend request sent!`, 'success');
|
||||
setQuery('');
|
||||
} catch (err) {
|
||||
addToast((err as Error).message, 'warning');
|
||||
}
|
||||
};
|
||||
|
||||
// No-op relationship change for search mode cards (useMemo re-derives from store)
|
||||
const noopRelationshipChange = useCallback(() => {}, []);
|
||||
|
||||
@@ -618,15 +600,6 @@ function AddFriendTab({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{connectModal && (
|
||||
<ConnectInstanceModal
|
||||
domain={connectModal.domain}
|
||||
targetDisplayName={connectModal.username}
|
||||
isReconnect={connectModal.isReconnect}
|
||||
onConnected={handleConnected}
|
||||
onCancel={() => setConnectModal(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -647,12 +620,6 @@ function UserDiscoverCard({
|
||||
const openModal = useUIStore((s) => s.openModal);
|
||||
const [actionLoading, setActionLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const addToast = useUIStore((s) => s.addToast);
|
||||
const [connectModal, setConnectModal] = useState<{
|
||||
domain: string;
|
||||
isReconnect: boolean;
|
||||
username: string;
|
||||
} | null>(null);
|
||||
|
||||
const baseName = user.username.includes('@') ? user.username.split('@')[0]! : user.username;
|
||||
const displayName = user.displayName ?? baseName;
|
||||
@@ -676,32 +643,15 @@ function UserDiscoverCard({
|
||||
const requestId = await sendFriendRequest(username);
|
||||
onRelationshipChange(user.id, user._instanceOrigin, 'outbound_pending', requestId);
|
||||
} catch (err) {
|
||||
if (err instanceof InstanceNotConnectedError) {
|
||||
setConnectModal({ domain: err.domain, isReconnect: false, username });
|
||||
} else if (err instanceof InstanceDisconnectedError) {
|
||||
setConnectModal({ domain: err.domain, isReconnect: true, username });
|
||||
} else {
|
||||
setError(err instanceof Error ? err.message : 'Failed to send request');
|
||||
const errorBody = (err as { body?: unknown })?.body;
|
||||
let code: string | undefined;
|
||||
let message: string | undefined;
|
||||
if (errorBody && typeof errorBody === 'object') {
|
||||
code = (errorBody as { error?: string }).error;
|
||||
message = (errorBody as { message?: string }).message;
|
||||
}
|
||||
} finally {
|
||||
setActionLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDiscoverConnected = async (result: 'new' | 'reconnect') => {
|
||||
const username = connectModal?.username;
|
||||
const domain = connectModal?.domain;
|
||||
setConnectModal(null);
|
||||
if (!username) return;
|
||||
|
||||
setActionLoading(true);
|
||||
try {
|
||||
const requestId = await sendFriendRequest(username);
|
||||
onRelationshipChange(user.id, user._instanceOrigin, 'outbound_pending', requestId);
|
||||
const verb = result === 'reconnect' ? 'Reconnected to' : 'Connected to';
|
||||
addToast(`${verb} ${domain} — friend request sent!`, 'success');
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to send request');
|
||||
const fallback = message ?? (err instanceof Error ? err.message : 'Failed to send request');
|
||||
setError(mapServerErrorToMessage(code, fallback, username));
|
||||
} finally {
|
||||
setActionLoading(false);
|
||||
}
|
||||
@@ -885,15 +835,6 @@ function UserDiscoverCard({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{connectModal && (
|
||||
<ConnectInstanceModal
|
||||
domain={connectModal.domain}
|
||||
targetDisplayName={user.displayName ?? baseName}
|
||||
isReconnect={connectModal.isReconnect}
|
||||
onConnected={handleDiscoverConnected}
|
||||
onCancel={() => setConnectModal(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user