import type { CommunicationRequest, Reference } from 'fhir/r4'; /** * Resolve the OTHER party in a CommunicationRequest thread, relative to whoever is being displayed. * * Finding B12 (act-as, ): this logic previously existed as two copy-pasted `useCallback`s - * `components/dashboard/Messages.tsx` and `pages/DashboardPage/MessagesPage/MessagesPage.tsx` - each * deriving "me" from `currentUser.fhir`, the LOGIN identity. While a caregiver is acting-as, the * threads on those surfaces belong to the SUBJECT, so a message the subject sent failed the "is that * me?" check and the subject was returned as their own counterparty. Both call sites now pass * `messagingIdentity` (the displayed patient), the same value CommunicationService scopes the threads * by, so the label cannot disagree with the data. * * It lives here as ONE pure function because duplication is what let the two drift in the first * place - the fix and the defect had the same root. Pure and reference-only, so it is trivially * testable without rendering either surface. * * Scope: CommunicationREQUEST-level identity only. A request's sender/recipient always reference the * Patient (self or subject), never a RelatedPerson - which is why request-level identity follows the * subject. MESSAGE-level sender attribution is a separate, deliberate judgment call left on * `currentUser.fhir`; see `CommunicationService.messagingIdentity.test.tsx`'s header. * * @param request the thread whose counterparty is wanted * @param selfReference the displayed identity as `ResourceType/id` (from `messagingIdentity`), or * `undefined` while it is still resolving - in which case the sender is returned, * matching the previous fallback rather than inventing a new one. */ export function resolveCounterpartyReference( request: CommunicationRequest, selfReference: string | undefined, ): Reference | undefined { const sender = request.sender; const senderRef = sender?.reference; if (senderRef && selfReference && senderRef === selfReference) { return request.recipient?.[0]; } return sender || request.recipient?.[0]; } /** * Older marker urls stay readable, newest first - mirroring the thread-type and read-receipt extensions. */ const ORGANIZATION_ID_EXTENSION_URLS = [ 'https://akashichealthrecords.com/fhir/StructureDefinition/organization-id', 'https://akashic.health/fhir/StructureDefinition/organization-id', ]; /** * The practice a thread belongs to. * * - for a FRONT-DESK POOL thread this is the nearest thing to a counterparty there is: the * conversation is with the office, so the practice's own name is the only honest thing to put beside it. * `resolveCounterpartyReference` above returns `undefined` for one of these, and that is correct rather * than broken - a pool thread has no person on the other end, by design. */ export function getThreadOrganizationId( request: CommunicationRequest | undefined, ): string | undefined { const extension = request?.extension?.find( (item) => !!item?.url && ORGANIZATION_ID_EXTENSION_URLS.includes(item.url), ) as { valueString?: string; valueCode?: string } | undefined; return extension?.valueString || extension?.valueCode || undefined; } /** `messagingIdentity` -> the `ResourceType/id` string the comparison above needs. */ export function toSelfReference( identity: { resourceType?: string; id?: string } | undefined, ): string | undefined { return identity?.resourceType && identity?.id ? `${identity.resourceType}/${identity.id}` : undefined; }