83 lines
1.9 KiB
TypeScript
83 lines
1.9 KiB
TypeScript
/** Shared invite-field email check (create-flow Invite your members). */
|
|
export const INVITE_EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
|
const COUNT_WORDS = [
|
|
"zero",
|
|
"one",
|
|
"two",
|
|
"three",
|
|
"four",
|
|
"five",
|
|
"six",
|
|
"seven",
|
|
"eight",
|
|
"nine",
|
|
"ten",
|
|
] as const;
|
|
|
|
/** Lowercase count for paste-result copy (`five`, or digits past ten). */
|
|
export function formatInviteCountWord(n: number): string {
|
|
if (Number.isInteger(n) && n >= 0 && n <= 10) {
|
|
return COUNT_WORDS[n] ?? String(n);
|
|
}
|
|
return String(n);
|
|
}
|
|
|
|
export function splitInvitePasteTokens(raw: string): string[] {
|
|
return raw
|
|
.split(/[\s,;]+/)
|
|
.map((token) => token.trim())
|
|
.filter((token) => token.length > 0);
|
|
}
|
|
|
|
export type ParseInvitePasteResult = {
|
|
added: string[];
|
|
invalidRemainder: string;
|
|
skippedDuplicate: number;
|
|
};
|
|
|
|
/**
|
|
* Split a pasted list into valid new addresses and leftovers that stay in the
|
|
* field. Tokens that are not emails, duplicates, or that would exceed `max`
|
|
* remain in {@link ParseInvitePasteResult.invalidRemainder}.
|
|
*/
|
|
export function parseInvitePaste(
|
|
raw: string,
|
|
existing: readonly string[],
|
|
max: number,
|
|
): ParseInvitePasteResult {
|
|
const tokens = splitInvitePasteTokens(raw);
|
|
const added: string[] = [];
|
|
const invalid: string[] = [];
|
|
const seen = new Set(
|
|
existing
|
|
.map((email) => email.trim().toLowerCase())
|
|
.filter((email) => email.length > 0),
|
|
);
|
|
let skippedDuplicate = 0;
|
|
|
|
for (const token of tokens) {
|
|
const lower = token.toLowerCase();
|
|
if (!INVITE_EMAIL_PATTERN.test(lower)) {
|
|
invalid.push(token);
|
|
continue;
|
|
}
|
|
if (seen.has(lower)) {
|
|
skippedDuplicate += 1;
|
|
continue;
|
|
}
|
|
if (existing.length + added.length >= max) {
|
|
invalid.push(token);
|
|
continue;
|
|
}
|
|
seen.add(lower);
|
|
added.push(lower);
|
|
}
|
|
|
|
return {
|
|
added,
|
|
invalidRemainder: invalid.join(", "),
|
|
skippedDuplicate,
|
|
};
|
|
}
|