feat(friends): always-visible Direct-Add row with resolved-form display
The Send-Friend-Request action row in the Add Friend tab previously appeared only when the typed query contained a non-edge @, leaving no way to fire a blind request for a bare local handle. Widen the gate to allow non-empty bare handles, keep the malformed @ shapes (@, @bob, bob@) hidden. When the typed query has no @, display the resolved form <query>@<window.location.host> so the user sees which instance the request will hit. Submission string is unchanged. Updates FriendsPage.test.tsx: inverts the now-stale 'does not show Direct Add row for plain usernames' test into the new positive assertion, and adds a separate test for the malformed @ shapes.
This commit is contained in:
@@ -254,7 +254,7 @@ describe('FriendsPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not show Direct Add row for plain usernames', async () => {
|
it('shows Direct Add row with resolved-form display for plain usernames', async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
renderFriendsPage();
|
renderFriendsPage();
|
||||||
await user.click(screen.getByText('Add Friend'));
|
await user.click(screen.getByText('Add Friend'));
|
||||||
@@ -262,6 +262,31 @@ describe('FriendsPage', () => {
|
|||||||
const input = screen.getByPlaceholderText(/Search or add by username/);
|
const input = screen.getByPlaceholderText(/Search or add by username/);
|
||||||
await user.type(input, 'marc');
|
await user.type(input, 'marc');
|
||||||
|
|
||||||
|
// Direct Add row appears with the resolved form `marc@<window.location.host>`.
|
||||||
|
// The exact host depends on jsdom (localhost:3000 by default), so match by prefix.
|
||||||
|
expect(screen.getByText(/Send friend request to/)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(new RegExp(`marc@${window.location.host.replace(/[.+?^${}()|[\]\\]/g, '\\$&')}`))).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show Direct Add row for malformed @ shapes', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderFriendsPage();
|
||||||
|
await user.click(screen.getByText('Add Friend'));
|
||||||
|
|
||||||
|
const input = screen.getByPlaceholderText(/Search or add by username/);
|
||||||
|
|
||||||
|
// Lone @
|
||||||
|
await user.type(input, '@');
|
||||||
|
expect(screen.queryByText(/Send friend request to/)).not.toBeInTheDocument();
|
||||||
|
await user.clear(input);
|
||||||
|
|
||||||
|
// Leading @ — only domain, no baseName
|
||||||
|
await user.type(input, '@bob');
|
||||||
|
expect(screen.queryByText(/Send friend request to/)).not.toBeInTheDocument();
|
||||||
|
await user.clear(input);
|
||||||
|
|
||||||
|
// Trailing @ — only baseName, no domain
|
||||||
|
await user.type(input, 'bob@');
|
||||||
expect(screen.queryByText(/Send friend request to/)).not.toBeInTheDocument();
|
expect(screen.queryByText(/Send friend request to/)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -462,9 +462,15 @@ function AddFriendTab({
|
|||||||
});
|
});
|
||||||
}, [rawSearchResults, friends, requests, selfIds, isSearchMode]);
|
}, [rawSearchResults, friends, requests, selfIds, isSearchMode]);
|
||||||
|
|
||||||
// Direct Add detection (synchronous, not debounced)
|
const trimmedQuery = query.trim();
|
||||||
const atIndex = query.lastIndexOf('@');
|
const directAt = trimmedQuery.lastIndexOf('@');
|
||||||
const showDirectAdd = atIndex > 0 && atIndex < query.length - 1;
|
const showDirectAdd = trimmedQuery.length > 0
|
||||||
|
&& (directAt === -1 || (directAt > 0 && directAt < trimmedQuery.length - 1));
|
||||||
|
// Bare handle gets the home host appended for display only — submission
|
||||||
|
// still uses the raw trimmed query.
|
||||||
|
const directAddDisplay = trimmedQuery.includes('@')
|
||||||
|
? trimmedQuery
|
||||||
|
: `${trimmedQuery}@${window.location.host}`;
|
||||||
|
|
||||||
// Direct Add handler
|
// Direct Add handler
|
||||||
const handleDirectAdd = async () => {
|
const handleDirectAdd = async () => {
|
||||||
@@ -552,7 +558,7 @@ function AddFriendTab({
|
|||||||
<path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
|
<path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
|
||||||
</svg>
|
</svg>
|
||||||
<div className="flex-1 min-w-0 text-sm text-txt-secondary">
|
<div className="flex-1 min-w-0 text-sm text-txt-secondary">
|
||||||
Send friend request to <span className="font-semibold text-txt-primary">{query.trim()}</span>
|
Send friend request to <span className="font-semibold text-txt-primary">{directAddDisplay}</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleDirectAdd}
|
onClick={handleDirectAdd}
|
||||||
|
|||||||
Reference in New Issue
Block a user