| 1 | /** |
| 2 | * Inviting someone into a workspace from People: who was typed, and what |
| 3 | * to search for as they type. Search finds people by username or name and |
| 4 | * shows only what a profile shows (a username, a name, an avatar), never an |
| 5 | * email address. No Workers or React imports, so it can be tested under Node. |
| 6 | */ |
| 7 | |
| 8 | /** Where the invite form asks for people as someone types (routes/people-json.ts). */ |
| 9 | export const PEOPLE_SEARCH_PATH = "/-/people.json"; |
| 10 | |
| 11 | /** The longest query the search takes. */ |
| 12 | const MAX_QUERY = 80; |
| 13 | |
| 14 | /** Whether `text` reads as an email address rather than a username. */ |
| 15 | export function looksLikeEmail(text: string): boolean { |
| 16 | const at = text.trim().indexOf("@"); |
| 17 | return at > 0; |
| 18 | } |
| 19 | |
| 20 | /** |
| 21 | * What to search for as someone types `text`: the text without a leading |
| 22 | * `@`, or null for nothing to search (empty, or an email address, which is |
| 23 | * never looked up). |
| 24 | */ |
| 25 | export function peopleQuery(text: string): string | null { |
| 26 | const trimmed = text.trim(); |
| 27 | if (!trimmed || looksLikeEmail(trimmed)) return null; |
| 28 | const query = trimmed.replace(/^@+/, "").slice(0, MAX_QUERY); |
| 29 | return query ? query : null; |
| 30 | } |
| 31 | |
| 32 | /** Who the invite form names: an email address, or a username. Null for nothing. */ |
| 33 | export function inviteTarget(text: string): { email: string } | { username: string } | null { |
| 34 | const trimmed = text.trim(); |
| 35 | if (!trimmed) return null; |
| 36 | if (looksLikeEmail(trimmed)) return { email: trimmed }; |
| 37 | const username = trimmed.replace(/^@+/, "").toLowerCase(); |
| 38 | return username ? { username } : null; |
| 39 | } |