From 7149e1ba04dc5e69bdb3624d4d85cec8d7895217 Mon Sep 17 00:00:00 2001 From: Studio-18 Date: Thu, 16 Oct 2025 19:15:34 -0700 Subject: [PATCH] fix: include pending open-match participants --- src/utils/participants.js | 112 +++++++++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 15 deletions(-) diff --git a/src/utils/participants.js b/src/utils/participants.js index fcc51734..a495e144 100644 --- a/src/utils/participants.js +++ b/src/utils/participants.js @@ -88,6 +88,38 @@ const buildIdentity = (item, keys = DEFAULT_IDENTITY_KEYS) => { return null; }; +const normalizeStatusValue = (value) => { + if (!value) return ""; + return value.toString().trim().toLowerCase(); +}; + +const INACTIVE_STATUS_VALUES = new Set([ + "left", + "removed", + "cancelled", + "canceled", + "declined", + "rejected", + "withdrawn", + "expired", + "pending", + "invited", +]); + +const PENDING_STATUS_VALUES = new Set(["pending", "invited"]); + +const JOIN_STATUS_VALUES = new Set([ + "joined", + "accepted", + "confirmed", + "on_roster", + "on-roster", + "on roster", + "active", +]); + +const BOOLEAN_TRUE_VALUES = new Set(["true", "yes", "1"]); + const hasIdentity = (item, keys = DEFAULT_IDENTITY_KEYS) => { if (!item || typeof item !== "object") return false; return buildIdentity(item, keys) !== null; @@ -140,7 +172,23 @@ const isParticipantActive = (participant) => { participant.status_reason, participant.statusReason, ]; - if (statusCandidates.some((value) => isInactiveStatus(value))) { + const normalizedStatuses = statusCandidates + .map((value) => normalizeStatusValue(value)) + .filter((value) => value); + + const hasJoinSignal = participantHasJoinSignal( + participant, + normalizedStatuses, + ); + + if ( + normalizedStatuses.some((status) => { + if (PENDING_STATUS_VALUES.has(status)) { + return !hasJoinSignal; + } + return INACTIVE_STATUS_VALUES.has(status); + }) + ) { return false; } @@ -225,20 +273,54 @@ const hasAnyValue = (item, keys = []) => { }; const isInactiveStatus = (value) => { - if (!value) return false; - const normalized = value.toString().trim().toLowerCase(); - return [ - "left", - "removed", - "cancelled", - "canceled", - "declined", - "rejected", - "withdrawn", - "expired", - "pending", - "invited", - ].includes(normalized); + const normalized = normalizeStatusValue(value); + if (!normalized) return false; + return INACTIVE_STATUS_VALUES.has(normalized); +}; + +const participantHasJoinSignal = (participant, normalizedStatuses = []) => { + if (!participant || typeof participant !== "object") return false; + + if ( + normalizedStatuses.some((status) => + JOIN_STATUS_VALUES.has(status), + ) + ) { + return true; + } + + if (hasAnyValue(participant, JOIN_METADATA_KEYS)) { + return true; + } + + const joinIndicators = [ + participant.joined, + participant.is_joined, + participant.isJoined, + participant.joined_status, + participant.joinedStatus, + ]; + + return joinIndicators.some((value) => { + if (value === null || value === undefined) { + return false; + } + if (typeof value === "boolean") { + return value; + } + if (typeof value === "number") { + return Number.isFinite(value) && value !== 0; + } + if (typeof value === "string") { + const normalized = normalizeStatusValue(value); + if (!normalized) return false; + return ( + JOIN_STATUS_VALUES.has(normalized) || + BOOLEAN_TRUE_VALUES.has(normalized) + ); + } + return true; + }); }; const isInviteActive = (invite) => {