import { parsePublishedDocumentForCommunityRuleDisplay } from "./publishedDocumentToDisplaySections"; import { formatInviteCountWord } from "./parseInviteEmails"; import { RULE_SECTION_CATEGORY } from "./ruleSectionsFromMethodSelections"; import sharedTerms from "../../messages/en/create/sharedTerms.json"; const DESCRIPTION_MAX = 140; /** Initials for the invite email disc when the group has no photo. */ export function communityInviteInitials(name: string): string { const words = name .trim() .split(/\s+/) .filter((word) => word.length > 0); if (words.length === 0) return "·"; if (words.length === 1) { const word = words[0] ?? ""; return word.slice(0, 2).toUpperCase(); } const first = words[0]?.charAt(0) ?? ""; const second = words[1]?.charAt(0) ?? ""; return `${first}${second}`.toUpperCase(); } /** Possessive for footer copy (`Share’s` / `Members’`). */ export function communityPossessive(name: string): string { const trimmed = name.trim(); if (trimmed.length === 0) return "this community’s"; if (/s$/i.test(trimmed)) return `${trimmed}’`; return `${trimmed}’s`; } /** Community description on the invite card, cut at 140 characters. */ export function clipCommunityInviteDescription( raw: string | null | undefined, ): string { const trimmed = typeof raw === "string" ? raw.trim() : ""; if (trimmed.length <= DESCRIPTION_MAX) return trimmed; return trimmed.slice(0, DESCRIPTION_MAX).trimEnd(); } /** Values vs method cards on a published Rule. Shared terms do not count. */ export function inviteRuleValueAndCardCounts(document: unknown): { values: number; cards: number; } { const sections = parsePublishedDocumentForCommunityRuleDisplay(document); let values = 0; let cards = 0; for (const section of sections) { const n = section.entries.length; if (section.categoryName === RULE_SECTION_CATEGORY.values) { values += n; } else if (section.categoryName !== sharedTerms.sectionTitle) { cards += n; } } return { values, cards }; } /** Tag on the invite Rule card (`3 VALUES AND 6 CARDS`). Shared terms do not count. */ export function inviteRuleCountsLabel(document: unknown): string { const { values, cards } = inviteRuleValueAndCardCounts(document); const valuePart = values === 1 ? "1 VALUE" : `${values} VALUES`; const cardPart = cards === 1 ? "1 CARD" : `${cards} CARDS`; return `${valuePart} AND ${cardPart}`; } /** Spoken counts for Make a copy (`three values and six cards`). */ export function inviteRuleCountsSpoken(document: unknown): string { const { values, cards } = inviteRuleValueAndCardCounts(document); const valueNoun = values === 1 ? "value" : "values"; const cardNoun = cards === 1 ? "card" : "cards"; return `${formatInviteCountWord(values)} ${valueNoun} and ${formatInviteCountWord(cards)} ${cardNoun}`; }