@adminide-stack/yantra-mobile 12.0.69-alpha.20 → 12.0.69-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import {isConnectorReply,stripConnectorConnect,parseConnectorConnect,isSiteLoginRequest,pickConnectorCredential,CONNECTOR_CONNECT_WEB_RETURN_PATH,isSiteLoginCredential,credentialSavedAt}from'common';var __defProp = Object.defineProperty;
1
+ import {parseConnectorConnect,isSiteLoginRequest,pickConnectorCredential,CONNECTOR_CONNECT_WEB_RETURN_PATH,isSiteLoginCredential,credentialSavedAt}from'common';export{displayConnectorConnectMessages,displayConnectorConnectText}from'common';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -30,21 +30,6 @@ function pendingConnectorConnect(messages) {
30
30
  }
31
31
  return null;
32
32
  }
33
- function displayConnectorConnectText(text, streaming) {
34
- const stripped = stripConnectorConnect(text);
35
- if (!streaming) return stripped;
36
- const open = stripped.indexOf("[CONNECTOR_CONNECT");
37
- return open >= 0 ? stripped.slice(0, open).trim() : stripped;
38
- }
39
- function displayConnectorConnectMessages(messages) {
40
- return messages.filter((msg) => !(msg.role === "user" && isConnectorReply(msg.content))).map((msg) => {
41
- if (msg.role !== "assistant") return msg;
42
- const content = displayConnectorConnectText(msg.content, false);
43
- return content === msg.content ? msg : __spreadProps(__spreadValues({}, msg), {
44
- content
45
- });
46
- });
47
- }
48
33
  function connectorConnectKey(pending) {
49
34
  return `${pending.messageIndex}:${pending.payload.requestId}`;
50
35
  }
@@ -191,4 +176,4 @@ function parkedHandbackCredential(rows, payload, credentialId, nowMs) {
191
176
  if (!fits) return null;
192
177
  const savedAt = credentialSavedAt(row);
193
178
  return savedAt > 0 && nowMs - savedAt <= PARKED_HANDBACK_MAX_AGE_MS ? row : null;
194
- }export{CONNECTORS_PATH,PARKED_HANDBACK_MAX_AGE_MS,VAULT_NEW_PATH,buildConnectorConnectUrl,connectorConnectActionLabel,connectorConnectKey,connectorConnectName,connectorConnectReason,connectorConnectTarget,credentialFingerprint,decideConnectorConnectOutcome,displayConnectorConnectMessages,displayConnectorConnectText,parkedHandbackCredential,pendingConnectorConnect,pickConnectorConnectCredential,snapshotConnectorCredentials};//# sourceMappingURL=connectorConnect.js.map
179
+ }export{CONNECTORS_PATH,PARKED_HANDBACK_MAX_AGE_MS,VAULT_NEW_PATH,buildConnectorConnectUrl,connectorConnectActionLabel,connectorConnectKey,connectorConnectName,connectorConnectReason,connectorConnectTarget,credentialFingerprint,decideConnectorConnectOutcome,parkedHandbackCredential,pendingConnectorConnect,pickConnectorConnectCredential,snapshotConnectorCredentials};//# sourceMappingURL=connectorConnect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connectorConnect.js","sources":["../../../src/features/chat/connectorConnect.ts"],"sourcesContent":["/**\n * The `[CONNECTOR_CONNECT]` card, mobile side.\n *\n * The marker and its replies are parsed by common's CONNECTOR_CONNECT_PROTOCOL,\n * the same module the web chat uses, so every marker shape the model drifts into\n * is read the same way on both. This file holds only what is specific to the\n * phone: which request is still waiting, the card's copy, the web page the card\n * opens (the phone cannot host the web's inline forms, so it opens them in an\n * in-app browser session), and how to tell, once that session ends, whether the\n * vault really changed.\n *\n * Host-only: this module imports `common`, which the federated surface remotes\n * must not bundle. A remote's chat gets its messages already cleaned\n * (displayConnectorConnectMessages) and its card as a host render prop.\n */\nimport {\n CONNECTOR_CONNECT_WEB_RETURN_PATH,\n credentialSavedAt,\n isConnectorReply,\n isSiteLoginCredential,\n isSiteLoginRequest,\n parseConnectorConnect,\n pickConnectorCredential,\n stripConnectorConnect,\n type ConnectorConnectHandback,\n type ConnectorConnectPayload,\n type ConnectorConnectionTarget,\n type ConnectorCredentialListRow,\n} from 'common';\n\n/** forms-stack's \"Save to vault\" page as the web app mounts it (AccountRoutes.AccountConnectorsVaultNew). */\nexport const VAULT_NEW_PATH = '/account/connectors/vault/new';\n\n/** A connect request still waiting on the user, and where it sits in the thread. */\nexport interface PendingConnectorConnect {\n payload: ConnectorConnectPayload;\n /** Index of the assistant message that carried the marker. */\n messageIndex: number;\n}\n\n/**\n * The connect request still awaiting an answer: the newest marker in the\n * assistant messages since the last user message. Any user message after it - the\n * `[CONNECTOR_CONNECTED:…]` / `[CONNECTOR_DECLINED:…]` reply the card sends, or\n * the user simply typing on - settles it, so a reloaded thread never brings back a\n * card that was already answered (the same rule as pendingAskUser).\n */\nexport function pendingConnectorConnect(\n messages: ReadonlyArray<{ role: string; content: string }>,\n): PendingConnectorConnect | null {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const payload = msg.role === 'assistant' ? parseConnectorConnect(msg.content) : null;\n if (payload) return { payload, messageIndex: i };\n }\n return null;\n}\n\n/**\n * Assistant prose with every connect marker taken out - the card stands in for\n * it. While the reply is still streaming, a marker that has started but not yet\n * closed is cut off at its start too, so its half-written JSON never flashes in\n * the bubble. A finished reply loses only complete marker shapes, so text that\n * merely starts like one is not swallowed for good.\n */\nexport function displayConnectorConnectText(text: string, streaming: boolean): string {\n const stripped = stripConnectorConnect(text);\n if (!streaming) return stripped;\n const open = stripped.indexOf('[CONNECTOR_CONNECT');\n return open >= 0 ? stripped.slice(0, open).trim() : stripped;\n}\n\n/**\n * A thread as the transcript shows it: the card's `[CONNECTOR_CONNECTED:…]` /\n * `[CONNECTOR_DECLINED:…]` replies are protocol, not something the user said, so\n * they go; assistant messages lose their connect markers and keep their prose (a\n * reply that was only a marker is left empty, which the transcript skips). Every\n * other field of a row is kept, ids included, so rows do not remount.\n *\n * The host runs this before handing a thread to ChatTranscript, so the transcript\n * (which the surface remotes bundle too) never needs the protocol parser.\n */\nexport function displayConnectorConnectMessages<T extends { role: string; content: string }>(\n messages: ReadonlyArray<T>,\n): T[] {\n return messages\n .filter((msg) => !(msg.role === 'user' && isConnectorReply(msg.content)))\n .map((msg) => {\n if (msg.role !== 'assistant') return msg;\n const content = displayConnectorConnectText(msg.content, false);\n return content === msg.content ? msg : { ...msg, content };\n });\n}\n\n/**\n * What a local \"answered\" mark is keyed on. The request id alone is not enough: a\n * marker that carries no id gets one derived from its text, so the same marker\n * repeated in a later turn has the same id, and keying on the id alone would hide\n * that new card too.\n */\nexport function connectorConnectKey(pending: PendingConnectorConnect): string {\n return `${pending.messageIndex}:${pending.payload.requestId}`;\n}\n\n/** A connector key as a readable name: `google-ads` -> `google ads` (the web's cleanup, minus the template lookup). */\nfunction readableConnector(connector: string): string {\n const cleaned = connector\n .replace(/-app-[a-z0-9]+$/i, '')\n .replace(/\\s*\\((?:app|api|oauth)\\)\\s*$/i, '')\n .replace(/[_-]+/g, ' ')\n .trim();\n return cleaned || connector;\n}\n\n/** Card title, and the name a new site login is saved under: \"Amazon login\", \"amazon.com login\", \"openai\". */\nexport function connectorConnectName(payload: ConnectorConnectPayload): string {\n if (payload.title) return payload.title;\n if (isSiteLoginRequest(payload)) return payload.form?.site ? `${payload.form.site} login` : 'Website login';\n return readableConnector(payload.connector);\n}\n\n/** The card's one line under the title; the agent's own reason wins. */\nexport function connectorConnectReason(payload: ConnectorConnectPayload, name: string): string {\n if (payload.reason) return payload.reason;\n if (isSiteLoginRequest(payload)) {\n return `Save your ${payload.form?.site ?? 'website'} sign-in to the vault so I can log in on your Computer.`;\n }\n return `Connect ${name} to use it from this chat.`;\n}\n\n/**\n * What the card looks for in the vault: the web card's target for the same request (common's\n * matchConnectorCredentials), minus the template's formId, which only the web can resolve. A site\n * login takes saved logins for its site only; the credential the agent named wins when it is one\n * of the org's and fits, else the most recently updated match - the row the agent signs in with.\n *\n * For a site login this is the same target the web card builds, so the pick is the web's. For a\n * catalog connector it is only the card's status (\"Connected\" / \"Update\"): without the formId a\n * name-only match can take another connector's credential (\"Azure OpenAI\" for OpenAI) or miss one\n * saved under another name, so the phone never acts on it - the web page it opens picks (see\n * buildConnectorConnectUrl, and pickConnectorConnectCredential for the status).\n */\nexport function connectorConnectTarget(payload: ConnectorConnectPayload): Omit<ConnectorConnectionTarget, 'id'> {\n const siteLogin = isSiteLoginRequest(payload);\n return {\n name: connectorConnectName(payload),\n siteLogin,\n site: siteLogin ? (payload.form?.site ?? payload.form?.loginUrl) : undefined,\n credentialId: payload.credentialId,\n };\n}\n\n/** A name as the matcher compares it (common's matchConnectorCredentials): lower case, letters and digits only. */\nconst matchKey = (name: string | undefined) => (name ?? '').toLowerCase().replace(/[^a-z0-9]/g, '');\n\n/**\n * The saved credential the phone's card stands on for a request - \"Update\" when there is one, and the one the card\n * measures the browser session against - or undefined.\n *\n * A site login: common's pick on connectorConnectTarget, the web card's own target, so the very row the web card\n * and the agent use. A catalog connector: the web card also matches the template's formId, which the phone cannot\n * resolve, so a card titled \"OpenAI API key\" missed the \"OpenAI\" credential the web card finds by formId and showed\n * \"Connect\" where the web shows \"Update\". A credential saved from a template is named for it, so after the card\n * title the phone also looks under the connector the agent named (`openai`, `google-calendar` as \"google\n * calendar\"); the first name that matches wins. Only the phone's label and its after-the-session check rest on\n * this: the web page the card opens makes the pick it edits (buildConnectorConnectUrl).\n */\nexport function pickConnectorConnectCredential<T extends ConnectorCredentialListRow>(\n rows: ReadonlyArray<T | null | undefined>,\n payload: ConnectorConnectPayload,\n): T | undefined {\n const target = connectorConnectTarget(payload);\n if (target.siteLogin) return pickConnectorCredential(rows, target);\n const tried = new Set<string>();\n for (const name of [target.name, readableConnector(payload.connector)]) {\n const key = matchKey(name);\n if (key && !tried.has(key)) {\n tried.add(key);\n const hit = pickConnectorCredential(rows, { ...target, name });\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** The primary action: a saved connection is updated in place, a new one is saved or connected. */\nexport function connectorConnectActionLabel(payload: ConnectorConnectPayload, connected: boolean): string {\n if (connected) return 'Update';\n return isSiteLoginRequest(payload) ? 'Save login' : 'Connect';\n}\n\n/** `a=1&b=2`, skipping empty values. Hand-rolled: React Native's URLSearchParams cannot be relied on. */\nfunction toQuery(params: ReadonlyArray<readonly [string, string | null | undefined]>): string {\n return params\n .filter((entry): entry is readonly [string, string] => Boolean(entry[1]?.trim()))\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value.trim())}`)\n .join('&');\n}\n\nexport interface ConnectorConnectUrlInput {\n /** The web app's origin (CLIENT_URL). */\n origin: string;\n payload: ConnectorConnectPayload;\n /** Card title; a site login is saved under it. */\n name: string;\n /**\n * A site login only: update THIS saved login in place (the card's \"Update\"); absent saves a new one. Never sent\n * for a catalog connector, whose page makes the pick itself (see buildConnectorConnectUrl).\n */\n credentialId?: string | null;\n /**\n * A site login only: the project the vault form works in - where a new login is saved, and for an update the\n * project the login LIVES in (the form refuses to change a login from any other project, and the card matches\n * the whole org's).\n */\n projectId?: string | null;\n}\n\n/** The web Connectors entry that resolves a connector to its template first (ConnectorsRedirect). */\nexport const CONNECTORS_PATH = '/account/connectors';\n\n/**\n * The web page the card opens.\n *\n * A site login opens forms-stack's \"Save to vault\" page. Given `credentialId` that\n * page UPDATES that credential (username / email and password alike) instead of\n * saving a duplicate next to it. `returnTo` sends it, once saved, to the web\n * return page that hands the credential id back to the app, and `requestId`\n * rides along so the hand-back names the request it answers.\n *\n * Any other connector opens the Connectors entry with the agent's `connector` and\n * `aliases` as they came, and the page there is the web card itself\n * (ConnectorCredentialEdit): it resolves them against the org's template pool with\n * the web card's own resolver (useCredentialTemplateByKey), so a display name like\n * \"Google Calendar\" or a drifted key reaches the same template on both, and then\n * makes the web card's pick with the template's formId - which saved credential is\n * this connector's, to update in place, or none, to connect a new one. The phone\n * cannot resolve that formId, so it sends no pick of its own: a name-only pick can be\n * another connector's credential (\"Azure OpenAI\" for OpenAI), which the form would\n * then overwrite, or miss one saved under another name and start a duplicate. Only\n * the agent's own `credentialId` rides along, as the hint the web card honours when\n * it fits. Once saved the page returns through the same return page as the vault\n * form, so the phone learns which credential it was.\n */\nexport function buildConnectorConnectUrl({\n origin,\n payload,\n name,\n credentialId,\n projectId,\n}: ConnectorConnectUrlInput): string {\n const base = origin.trim().replace(/\\/+$/, '');\n if (isSiteLoginRequest(payload)) {\n const query = toQuery([\n ['name', name],\n ['site', payload.form?.site],\n ['loginUrl', payload.form?.loginUrl],\n ['agent', payload.form?.agentName],\n ['credentialId', credentialId],\n ['projectId', projectId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ]);\n return `${base}${VAULT_NEW_PATH}?${query}`;\n }\n const aliases = (payload.aliases ?? []).map((alias) => ['aliases', alias] as const);\n return `${base}${CONNECTORS_PATH}?${toQuery([\n ['connector', payload.connector],\n ...aliases,\n ['title', payload.title],\n ['credentialId', payload.credentialId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ])}`;\n}\n\n/** A saved credential as the card reads it: the matched fields, its version (`updatedAt`) and where it lives. */\nexport type CredentialVersionRow = ConnectorCredentialListRow;\n\n/**\n * A saved credential's identity AND version. An update keeps the id but moves\n * `updatedAt`, so comparing these before and after the browser tells a real save\n * apart from a closed browser.\n */\nexport function credentialFingerprint(row: CredentialVersionRow | null | undefined): string | null {\n if (!row?.id) return null;\n return `${row.id}@${row.updatedAt == null ? '' : String(row.updatedAt)}`;\n}\n\n/** The vault as it stood when the browser opened: every credential's version, and the one being updated. */\nexport interface ConnectorConnectBefore {\n /** Credential id -> fingerprint, for every row the read returned. */\n versions: Record<string, string>;\n /** The credential the card opened the form ON (\"Update\"), if any. */\n targetId?: string | null;\n}\n\n/** Record the vault before the browser opens, so the check afterwards can tell what the session changed. */\nexport function snapshotConnectorCredentials(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n targetId?: string | null,\n): ConnectorConnectBefore {\n const versions: Record<string, string> = {};\n for (const row of rows) {\n const fingerprint = credentialFingerprint(row);\n if (row?.id && fingerprint) versions[row.id] = fingerprint;\n }\n return { versions, targetId: targetId ?? null };\n}\n\nexport type ConnectorConnectOutcomeDecision =\n | { kind: 'connected'; credentialId?: string }\n /** Nothing changed in the vault: the card stays up, the agent is told nothing. */\n | { kind: 'unchanged' };\n\n/**\n * Whether the browser session produced a connection, and with which credential.\n *\n * 1. The session ended on the app's return URL: the vault page saved, and says\n * which credential. That is the answer, unless it names a different request.\n * 2. Otherwise (the person closed the browser, or the connector's page has no way\n * back to the app) the vault is read again and compared with `before`:\n * - the credential the form was opened ON has a newer version: updated in place\n * (found by id, so an edit that also renamed it still counts);\n * - the credential matching this request (formId / name) is new or newer;\n * - exactly one active credential appeared that was not there before: the\n * session created it, under whatever name the person gave it.\n * A credential some other flow merely touched is never taken, and a card never\n * claims a connection it cannot see.\n */\nexport function decideConnectorConnectOutcome({\n requestId,\n handback,\n before,\n after,\n}: {\n requestId: string;\n handback: ConnectorConnectHandback | null;\n /** The vault before the browser opened; without it nothing but a hand-back counts. */\n before: ConnectorConnectBefore | null;\n /** The vault after the browser closed, re-read from the server; null when that read failed. */\n after: { rows: ReadonlyArray<CredentialVersionRow | null | undefined>; match: CredentialVersionRow | null } | null;\n}): ConnectorConnectOutcomeDecision {\n if (handback && (!handback.requestId || handback.requestId === requestId)) {\n return { kind: 'connected', credentialId: handback.credentialId ?? after?.match?.id ?? undefined };\n }\n if (!before || !after) return { kind: 'unchanged' };\n const { versions, targetId } = before;\n const changed = (row: CredentialVersionRow | null | undefined): row is CredentialVersionRow => {\n const now = credentialFingerprint(row);\n return Boolean(now && row?.id && versions[row.id] !== now);\n };\n if (targetId) {\n const target = after.rows.find((row) => row?.id === targetId);\n if (target?.status === 'active' && changed(target)) return { kind: 'connected', credentialId: targetId };\n }\n if (changed(after.match)) return { kind: 'connected', credentialId: after.match.id ?? undefined };\n const created = after.rows.filter((row): row is CredentialVersionRow =>\n Boolean(row?.id && row.status === 'active' && !versions[row.id]),\n );\n if (created.length === 1) return { kind: 'connected', credentialId: created[0].id ?? undefined };\n return { kind: 'unchanged' };\n}\n\n/**\n * How long after its save a hand-back that reached the app with no browser session waiting is still taken: the\n * session the app was killed during (Android kills it behind Custom Tabs, and the return page cold-starts it).\n */\nexport const PARKED_HANDBACK_MAX_AGE_MS = 30 * 60 * 1000;\n\n/**\n * The credential a PARKED hand-back names, when it may be taken as this request's answer; null otherwise.\n *\n * Anyone can open the app's scheme URL, and the vault snapshot the card took before the browser opened died with\n * the app, so \"the vault has it\" is not enough. The credential must be:\n * - an ACTIVE credential of the org (`rows` is a fresh read);\n * - one this request could have saved: for a site login, the login the card itself picks for the request with\n * that id named (its site or name; never another site's login or a catalog credential); for a catalog\n * connector, a connector credential, never a website login - the phone cannot resolve the template, so a name\n * alone cannot rule a connector credential in or out (see connectorConnectTarget);\n * - saved in the last PARKED_HANDBACK_MAX_AGE_MS, the stand-in for \"changed during the session\": every save on\n * the web page moves `updatedAt`, while an id lifted from anywhere else names a credential saved long ago.\n */\nexport function parkedHandbackCredential(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n payload: ConnectorConnectPayload,\n credentialId: string | null | undefined,\n nowMs: number,\n): CredentialVersionRow | null {\n if (!credentialId) return null;\n const row = rows.find((r) => r?.id === credentialId && r.status === 'active');\n if (!row) return null;\n const fits = isSiteLoginRequest(payload)\n ? pickConnectorCredential(rows, { ...connectorConnectTarget(payload), credentialId })?.id === credentialId\n : !isSiteLoginCredential(row);\n if (!fits) return null;\n const savedAt = credentialSavedAt(row);\n return savedAt > 0 && nowMs - savedAt <= PARKED_HANDBACK_MAX_AGE_MS ? row : null;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connectorConnect.js","sources":["../../../src/features/chat/connectorConnect.ts"],"sourcesContent":["/**\n * The `[CONNECTOR_CONNECT]` card, mobile side.\n *\n * The marker and its replies are parsed by common's CONNECTOR_CONNECT_PROTOCOL,\n * the same module the web chat uses, so every marker shape the model drifts into\n * is read the same way on both. This file holds only what is specific to the\n * phone: which request is still waiting, the card's copy, the web page the card\n * opens (the phone cannot host the web's inline forms, so it opens them in an\n * in-app browser session), and how to tell, once that session ends, whether the\n * vault really changed.\n *\n * Host-only: this module imports `common`, which the federated surface remotes\n * must not bundle. A remote's chat gets its messages already cleaned\n * (displayConnectorConnectMessages) and its card as a host render prop.\n */\nimport {\n CONNECTOR_CONNECT_WEB_RETURN_PATH,\n credentialSavedAt,\n isSiteLoginCredential,\n isSiteLoginRequest,\n parseConnectorConnect,\n pickConnectorCredential,\n type ConnectorConnectHandback,\n type ConnectorConnectPayload,\n type ConnectorConnectionTarget,\n type ConnectorCredentialListRow,\n} from 'common';\n\n/** How a transcript shows a thread with connect markers in it - shared with the web host's surface chat. */\nexport { displayConnectorConnectMessages, displayConnectorConnectText } from 'common';\n\n/** forms-stack's \"Save to vault\" page as the web app mounts it (AccountRoutes.AccountConnectorsVaultNew). */\nexport const VAULT_NEW_PATH = '/account/connectors/vault/new';\n\n/** A connect request still waiting on the user, and where it sits in the thread. */\nexport interface PendingConnectorConnect {\n payload: ConnectorConnectPayload;\n /** Index of the assistant message that carried the marker. */\n messageIndex: number;\n}\n\n/**\n * The connect request still awaiting an answer: the newest marker in the\n * assistant messages since the last user message. Any user message after it - the\n * `[CONNECTOR_CONNECTED:…]` / `[CONNECTOR_DECLINED:…]` reply the card sends, or\n * the user simply typing on - settles it, so a reloaded thread never brings back a\n * card that was already answered (the same rule as pendingAskUser).\n */\nexport function pendingConnectorConnect(\n messages: ReadonlyArray<{ role: string; content: string }>,\n): PendingConnectorConnect | null {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const payload = msg.role === 'assistant' ? parseConnectorConnect(msg.content) : null;\n if (payload) return { payload, messageIndex: i };\n }\n return null;\n}\n\n/**\n * What a local \"answered\" mark is keyed on. The request id alone is not enough: a\n * marker that carries no id gets one derived from its text, so the same marker\n * repeated in a later turn has the same id, and keying on the id alone would hide\n * that new card too.\n */\nexport function connectorConnectKey(pending: PendingConnectorConnect): string {\n return `${pending.messageIndex}:${pending.payload.requestId}`;\n}\n\n/** A connector key as a readable name: `google-ads` -> `google ads` (the web's cleanup, minus the template lookup). */\nfunction readableConnector(connector: string): string {\n const cleaned = connector\n .replace(/-app-[a-z0-9]+$/i, '')\n .replace(/\\s*\\((?:app|api|oauth)\\)\\s*$/i, '')\n .replace(/[_-]+/g, ' ')\n .trim();\n return cleaned || connector;\n}\n\n/** Card title, and the name a new site login is saved under: \"Amazon login\", \"amazon.com login\", \"openai\". */\nexport function connectorConnectName(payload: ConnectorConnectPayload): string {\n if (payload.title) return payload.title;\n if (isSiteLoginRequest(payload)) return payload.form?.site ? `${payload.form.site} login` : 'Website login';\n return readableConnector(payload.connector);\n}\n\n/** The card's one line under the title; the agent's own reason wins. */\nexport function connectorConnectReason(payload: ConnectorConnectPayload, name: string): string {\n if (payload.reason) return payload.reason;\n if (isSiteLoginRequest(payload)) {\n return `Save your ${payload.form?.site ?? 'website'} sign-in to the vault so I can log in on your Computer.`;\n }\n return `Connect ${name} to use it from this chat.`;\n}\n\n/**\n * What the card looks for in the vault: the web card's target for the same request (common's\n * matchConnectorCredentials), minus the template's formId, which only the web can resolve. A site\n * login takes saved logins for its site only; the credential the agent named wins when it is one\n * of the org's and fits, else the most recently updated match - the row the agent signs in with.\n *\n * For a site login this is the same target the web card builds, so the pick is the web's. For a\n * catalog connector it is only the card's status (\"Connected\" / \"Update\"): without the formId a\n * name-only match can take another connector's credential (\"Azure OpenAI\" for OpenAI) or miss one\n * saved under another name, so the phone never acts on it - the web page it opens picks (see\n * buildConnectorConnectUrl, and pickConnectorConnectCredential for the status).\n */\nexport function connectorConnectTarget(payload: ConnectorConnectPayload): Omit<ConnectorConnectionTarget, 'id'> {\n const siteLogin = isSiteLoginRequest(payload);\n return {\n name: connectorConnectName(payload),\n siteLogin,\n site: siteLogin ? (payload.form?.site ?? payload.form?.loginUrl) : undefined,\n credentialId: payload.credentialId,\n };\n}\n\n/** A name as the matcher compares it (common's matchConnectorCredentials): lower case, letters and digits only. */\nconst matchKey = (name: string | undefined) => (name ?? '').toLowerCase().replace(/[^a-z0-9]/g, '');\n\n/**\n * The saved credential the phone's card stands on for a request - \"Update\" when there is one, and the one the card\n * measures the browser session against - or undefined.\n *\n * A site login: common's pick on connectorConnectTarget, the web card's own target, so the very row the web card\n * and the agent use. A catalog connector: the web card also matches the template's formId, which the phone cannot\n * resolve, so a card titled \"OpenAI API key\" missed the \"OpenAI\" credential the web card finds by formId and showed\n * \"Connect\" where the web shows \"Update\". A credential saved from a template is named for it, so after the card\n * title the phone also looks under the connector the agent named (`openai`, `google-calendar` as \"google\n * calendar\"); the first name that matches wins. Only the phone's label and its after-the-session check rest on\n * this: the web page the card opens makes the pick it edits (buildConnectorConnectUrl).\n */\nexport function pickConnectorConnectCredential<T extends ConnectorCredentialListRow>(\n rows: ReadonlyArray<T | null | undefined>,\n payload: ConnectorConnectPayload,\n): T | undefined {\n const target = connectorConnectTarget(payload);\n if (target.siteLogin) return pickConnectorCredential(rows, target);\n const tried = new Set<string>();\n for (const name of [target.name, readableConnector(payload.connector)]) {\n const key = matchKey(name);\n if (key && !tried.has(key)) {\n tried.add(key);\n const hit = pickConnectorCredential(rows, { ...target, name });\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** The primary action: a saved connection is updated in place, a new one is saved or connected. */\nexport function connectorConnectActionLabel(payload: ConnectorConnectPayload, connected: boolean): string {\n if (connected) return 'Update';\n return isSiteLoginRequest(payload) ? 'Save login' : 'Connect';\n}\n\n/** `a=1&b=2`, skipping empty values. Hand-rolled: React Native's URLSearchParams cannot be relied on. */\nfunction toQuery(params: ReadonlyArray<readonly [string, string | null | undefined]>): string {\n return params\n .filter((entry): entry is readonly [string, string] => Boolean(entry[1]?.trim()))\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value.trim())}`)\n .join('&');\n}\n\nexport interface ConnectorConnectUrlInput {\n /** The web app's origin (CLIENT_URL). */\n origin: string;\n payload: ConnectorConnectPayload;\n /** Card title; a site login is saved under it. */\n name: string;\n /**\n * A site login only: update THIS saved login in place (the card's \"Update\"); absent saves a new one. Never sent\n * for a catalog connector, whose page makes the pick itself (see buildConnectorConnectUrl).\n */\n credentialId?: string | null;\n /**\n * A site login only: the project the vault form works in - where a new login is saved, and for an update the\n * project the login LIVES in (the form refuses to change a login from any other project, and the card matches\n * the whole org's).\n */\n projectId?: string | null;\n}\n\n/** The web Connectors entry that resolves a connector to its template first (ConnectorsRedirect). */\nexport const CONNECTORS_PATH = '/account/connectors';\n\n/**\n * The web page the card opens.\n *\n * A site login opens forms-stack's \"Save to vault\" page. Given `credentialId` that\n * page UPDATES that credential (username / email and password alike) instead of\n * saving a duplicate next to it. `returnTo` sends it, once saved, to the web\n * return page that hands the credential id back to the app, and `requestId`\n * rides along so the hand-back names the request it answers.\n *\n * Any other connector opens the Connectors entry with the agent's `connector` and\n * `aliases` as they came, and the page there is the web card itself\n * (ConnectorCredentialEdit): it resolves them against the org's template pool with\n * the web card's own resolver (useCredentialTemplateByKey), so a display name like\n * \"Google Calendar\" or a drifted key reaches the same template on both, and then\n * makes the web card's pick with the template's formId - which saved credential is\n * this connector's, to update in place, or none, to connect a new one. The phone\n * cannot resolve that formId, so it sends no pick of its own: a name-only pick can be\n * another connector's credential (\"Azure OpenAI\" for OpenAI), which the form would\n * then overwrite, or miss one saved under another name and start a duplicate. Only\n * the agent's own `credentialId` rides along, as the hint the web card honours when\n * it fits. Once saved the page returns through the same return page as the vault\n * form, so the phone learns which credential it was.\n */\nexport function buildConnectorConnectUrl({\n origin,\n payload,\n name,\n credentialId,\n projectId,\n}: ConnectorConnectUrlInput): string {\n const base = origin.trim().replace(/\\/+$/, '');\n if (isSiteLoginRequest(payload)) {\n const query = toQuery([\n ['name', name],\n ['site', payload.form?.site],\n ['loginUrl', payload.form?.loginUrl],\n ['agent', payload.form?.agentName],\n ['credentialId', credentialId],\n ['projectId', projectId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ]);\n return `${base}${VAULT_NEW_PATH}?${query}`;\n }\n const aliases = (payload.aliases ?? []).map((alias) => ['aliases', alias] as const);\n return `${base}${CONNECTORS_PATH}?${toQuery([\n ['connector', payload.connector],\n ...aliases,\n ['title', payload.title],\n ['credentialId', payload.credentialId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ])}`;\n}\n\n/** A saved credential as the card reads it: the matched fields, its version (`updatedAt`) and where it lives. */\nexport type CredentialVersionRow = ConnectorCredentialListRow;\n\n/**\n * A saved credential's identity AND version. An update keeps the id but moves\n * `updatedAt`, so comparing these before and after the browser tells a real save\n * apart from a closed browser.\n */\nexport function credentialFingerprint(row: CredentialVersionRow | null | undefined): string | null {\n if (!row?.id) return null;\n return `${row.id}@${row.updatedAt == null ? '' : String(row.updatedAt)}`;\n}\n\n/** The vault as it stood when the browser opened: every credential's version, and the one being updated. */\nexport interface ConnectorConnectBefore {\n /** Credential id -> fingerprint, for every row the read returned. */\n versions: Record<string, string>;\n /** The credential the card opened the form ON (\"Update\"), if any. */\n targetId?: string | null;\n}\n\n/** Record the vault before the browser opens, so the check afterwards can tell what the session changed. */\nexport function snapshotConnectorCredentials(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n targetId?: string | null,\n): ConnectorConnectBefore {\n const versions: Record<string, string> = {};\n for (const row of rows) {\n const fingerprint = credentialFingerprint(row);\n if (row?.id && fingerprint) versions[row.id] = fingerprint;\n }\n return { versions, targetId: targetId ?? null };\n}\n\nexport type ConnectorConnectOutcomeDecision =\n | { kind: 'connected'; credentialId?: string }\n /** Nothing changed in the vault: the card stays up, the agent is told nothing. */\n | { kind: 'unchanged' };\n\n/**\n * Whether the browser session produced a connection, and with which credential.\n *\n * 1. The session ended on the app's return URL: the vault page saved, and says\n * which credential. That is the answer, unless it names a different request.\n * 2. Otherwise (the person closed the browser, or the connector's page has no way\n * back to the app) the vault is read again and compared with `before`:\n * - the credential the form was opened ON has a newer version: updated in place\n * (found by id, so an edit that also renamed it still counts);\n * - the credential matching this request (formId / name) is new or newer;\n * - exactly one active credential appeared that was not there before: the\n * session created it, under whatever name the person gave it.\n * A credential some other flow merely touched is never taken, and a card never\n * claims a connection it cannot see.\n */\nexport function decideConnectorConnectOutcome({\n requestId,\n handback,\n before,\n after,\n}: {\n requestId: string;\n handback: ConnectorConnectHandback | null;\n /** The vault before the browser opened; without it nothing but a hand-back counts. */\n before: ConnectorConnectBefore | null;\n /** The vault after the browser closed, re-read from the server; null when that read failed. */\n after: { rows: ReadonlyArray<CredentialVersionRow | null | undefined>; match: CredentialVersionRow | null } | null;\n}): ConnectorConnectOutcomeDecision {\n if (handback && (!handback.requestId || handback.requestId === requestId)) {\n return { kind: 'connected', credentialId: handback.credentialId ?? after?.match?.id ?? undefined };\n }\n if (!before || !after) return { kind: 'unchanged' };\n const { versions, targetId } = before;\n const changed = (row: CredentialVersionRow | null | undefined): row is CredentialVersionRow => {\n const now = credentialFingerprint(row);\n return Boolean(now && row?.id && versions[row.id] !== now);\n };\n if (targetId) {\n const target = after.rows.find((row) => row?.id === targetId);\n if (target?.status === 'active' && changed(target)) return { kind: 'connected', credentialId: targetId };\n }\n if (changed(after.match)) return { kind: 'connected', credentialId: after.match.id ?? undefined };\n const created = after.rows.filter((row): row is CredentialVersionRow =>\n Boolean(row?.id && row.status === 'active' && !versions[row.id]),\n );\n if (created.length === 1) return { kind: 'connected', credentialId: created[0].id ?? undefined };\n return { kind: 'unchanged' };\n}\n\n/**\n * How long after its save a hand-back that reached the app with no browser session waiting is still taken: the\n * session the app was killed during (Android kills it behind Custom Tabs, and the return page cold-starts it).\n */\nexport const PARKED_HANDBACK_MAX_AGE_MS = 30 * 60 * 1000;\n\n/**\n * The credential a PARKED hand-back names, when it may be taken as this request's answer; null otherwise.\n *\n * Anyone can open the app's scheme URL, and the vault snapshot the card took before the browser opened died with\n * the app, so \"the vault has it\" is not enough. The credential must be:\n * - an ACTIVE credential of the org (`rows` is a fresh read);\n * - one this request could have saved: for a site login, the login the card itself picks for the request with\n * that id named (its site or name; never another site's login or a catalog credential); for a catalog\n * connector, a connector credential, never a website login - the phone cannot resolve the template, so a name\n * alone cannot rule a connector credential in or out (see connectorConnectTarget);\n * - saved in the last PARKED_HANDBACK_MAX_AGE_MS, the stand-in for \"changed during the session\": every save on\n * the web page moves `updatedAt`, while an id lifted from anywhere else names a credential saved long ago.\n */\nexport function parkedHandbackCredential(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n payload: ConnectorConnectPayload,\n credentialId: string | null | undefined,\n nowMs: number,\n): CredentialVersionRow | null {\n if (!credentialId) return null;\n const row = rows.find((r) => r?.id === credentialId && r.status === 'active');\n if (!row) return null;\n const fits = isSiteLoginRequest(payload)\n ? pickConnectorCredential(rows, { ...connectorConnectTarget(payload), credentialId })?.id === credentialId\n : !isSiteLoginCredential(row);\n if (!fits) return null;\n const savedAt = credentialSavedAt(row);\n return savedAt > 0 && nowMs - savedAt <= PARKED_HANDBACK_MAX_AGE_MS ? row : null;\n}\n"],"names":[],"mappings":"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"}
@@ -36,7 +36,9 @@ function ContactsScreen({
36
36
  onOpenSettings,
37
37
  onStartChat,
38
38
  onInvite,
39
- onDismissError
39
+ onDismissError,
40
+ limited = false,
41
+ onChooseMoreContacts
40
42
  }) {
41
43
  const [query, setQuery] = React.useState("");
42
44
  const filterContact = React.useCallback((c) => {
@@ -97,9 +99,20 @@ function ContactsScreen({
97
99
  }], children: "Checking your contacts\u2026" })
98
100
  ] }));
99
101
  }
102
+ const listHeader = permission === "granted" && limited ? /* @__PURE__ */ jsxs(View, { children: [
103
+ /* @__PURE__ */ jsxs(View, { style: [styles.limitedNote, {
104
+ backgroundColor: theme.surface
105
+ }], children: [
106
+ /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "account-lock-outline", size: 20, color: theme.textMuted }),
107
+ /* @__PURE__ */ jsx(Text, { style: [styles.limitedText, {
108
+ color: theme.textMuted
109
+ }], children: "You shared only some contacts with Yantra. People you did not share will not show here." })
110
+ ] }),
111
+ onChooseMoreContacts ? /* @__PURE__ */ jsx(ActionRow, { icon: "account-multiple-plus-outline", label: "Choose more contacts", theme, onPress: onChooseMoreContacts, testID: "chat-contacts-choose-more" }) : null
112
+ ] }) : null;
100
113
  return shell(/* @__PURE__ */ jsxs(Fragment, { children: [
101
114
  /* @__PURE__ */ jsx(InlineBanner, { message: error, theme, onDismiss: onDismissError }),
102
- /* @__PURE__ */ jsx(SectionList, { sections, keyExtractor: (item) => item.id, stickySectionHeadersEnabled: false, renderSectionHeader: ({
115
+ /* @__PURE__ */ jsx(SectionList, { ListHeaderComponent: listHeader, sections, keyExtractor: (item) => item.id, stickySectionHeadersEnabled: false, renderSectionHeader: ({
103
116
  section
104
117
  }) => /* @__PURE__ */ jsx(Text, { style: [styles.sectionTitle, {
105
118
  color: theme.textMuted,
@@ -107,12 +120,35 @@ function ContactsScreen({
107
120
  }], children: section.title }), renderItem: ({
108
121
  item,
109
122
  section
110
- }) => /* @__PURE__ */ jsx(ContactRow, { contact: item, theme, busy: busyContactId === item.id, mode: section.key === "here" ? "chat" : "invite", onPress: () => section.key === "here" ? onStartChat(item) : onInvite(item) }), ListEmptyComponent: /* @__PURE__ */ jsx(View, { style: styles.centerFill, children: /* @__PURE__ */ jsx(EmptyState, { icon: "account-search-outline", title: "No contacts found", detail: "Add phone numbers to your contacts, or search with a different name.", theme, action: {
123
+ }) => /* @__PURE__ */ jsx(ContactRow, { contact: item, theme, busy: busyContactId === item.id, mode: section.key === "here" ? "chat" : "invite", onPress: () => section.key === "here" ? onStartChat(item) : onInvite(item) }), ListEmptyComponent: /* @__PURE__ */ jsx(View, { style: styles.centerFill, children: /* @__PURE__ */ jsx(EmptyState, { icon: "account-search-outline", title: "No contacts found", detail: limited && !query.trim() ? "Choose more contacts to share with Yantra." : "Add phone numbers to your contacts, or search with a different name.", theme, action: {
111
124
  label: "Back to chats",
112
125
  onPress: onBack
113
126
  } }) }), contentContainerStyle: sections.length === 0 ? styles.listEmptyGrow : void 0, testID: "chat-contacts-list" })
114
127
  ] }));
115
128
  }
129
+ function ActionRow({
130
+ icon,
131
+ label,
132
+ theme,
133
+ onPress,
134
+ testID
135
+ }) {
136
+ return /* @__PURE__ */ jsx(Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, style: ({
137
+ pressed
138
+ }) => ({
139
+ width: "100%",
140
+ backgroundColor: pressed ? theme.surface : theme.bg
141
+ }), testID, children: /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
142
+ /* @__PURE__ */ jsx(View, { style: [styles.actionIcon, {
143
+ backgroundColor: theme.surface
144
+ }], children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: icon, size: 22, color: theme.accent }) }),
145
+ /* @__PURE__ */ jsx(View, { style: [styles.rowBody, {
146
+ borderBottomColor: theme.border
147
+ }], children: /* @__PURE__ */ jsx(Text, { style: [styles.rowName, {
148
+ color: theme.accent
149
+ }], children: label }) })
150
+ ] }) });
151
+ }
116
152
  function ContactRow({
117
153
  contact,
118
154
  theme,
@@ -210,6 +246,28 @@ const styles = StyleSheet.create({
210
246
  fontSize: 13,
211
247
  marginTop: 2
212
248
  },
249
+ actionIcon: {
250
+ width: 44,
251
+ height: 44,
252
+ borderRadius: 22,
253
+ alignItems: "center",
254
+ justifyContent: "center"
255
+ },
256
+ limitedNote: {
257
+ flexDirection: "row",
258
+ alignItems: "flex-start",
259
+ marginHorizontal: 16,
260
+ marginTop: 4,
261
+ marginBottom: 4,
262
+ padding: 12,
263
+ borderRadius: 10
264
+ },
265
+ limitedText: {
266
+ flex: 1,
267
+ fontSize: 13,
268
+ lineHeight: 18,
269
+ marginLeft: 10
270
+ },
213
271
  inviteChip: {
214
272
  borderWidth: 1,
215
273
  borderRadius: 14,
@@ -1 +1 @@
1
- {"version":3,"file":"ContactsScreen.js","sources":["../../../src/features/messaging/ContactsScreen.tsx"],"sourcesContent":["/**\n * \"Find people you know\" - the contacts sync and invite screen.\n */\nimport * as React from 'react';\nimport { ActivityIndicator, Pressable, SectionList, StyleSheet, Text, TextInput, View } from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Avatar, EmptyState, InlineBanner, type ChatTheme } from './ChatComponents';\nimport type { MatchedContact } from './useContactMatches';\nimport type { ChatPermissionStatus } from './chatHost';\n\nfunction ContactsHeader({ theme, onBack }: { theme: ChatTheme; onBack: () => void }) {\n return (\n <SafeAreaView edges={['top']} style={{ backgroundColor: theme.headerBg }}>\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 8,\n paddingBottom: 8,\n borderBottomWidth: StyleSheet.hairlineWidth,\n borderBottomColor: theme.border,\n backgroundColor: theme.headerBg,\n }}\n >\n <Pressable onPress={onBack} hitSlop={10} accessibilityRole=\"button\" accessibilityLabel=\"Back\">\n <MaterialCommunityIcons name=\"chevron-left\" size={28} color={theme.accent} />\n </Pressable>\n <Text\n style={{\n flex: 1,\n fontSize: 20,\n fontWeight: '700',\n color: theme.text,\n marginLeft: 8,\n }}\n >\n Select contact\n </Text>\n </View>\n </SafeAreaView>\n );\n}\n\nexport function ContactsScreen({\n permission,\n loading,\n error,\n onYantra,\n invitable,\n theme,\n busyContactId,\n onBack,\n onRequestAccess,\n onOpenSettings,\n onStartChat,\n onInvite,\n onDismissError,\n}: {\n permission: ChatPermissionStatus | 'unsupported';\n loading: boolean;\n error: string | null;\n onYantra: MatchedContact[];\n invitable: MatchedContact[];\n theme: ChatTheme;\n busyContactId: string | null;\n onBack: () => void;\n onRequestAccess: () => void;\n onOpenSettings: () => void;\n onStartChat: (contact: MatchedContact) => void;\n onInvite: (contact: MatchedContact) => void;\n onDismissError: () => void;\n}) {\n const [query, setQuery] = React.useState('');\n\n const filterContact = React.useCallback(\n (c: MatchedContact) => {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n const name = (c.match?.displayName || c.displayName).toLowerCase();\n const qDigits = q.replace(/\\D/g, '');\n return (\n name.includes(q) ||\n (qDigits.length > 0 &&\n [...c.e164, ...(c.rawNumbers ?? [])].some((n) => n.replace(/\\D/g, '').includes(qDigits)))\n );\n },\n [query],\n );\n\n const sections = React.useMemo(\n () =>\n [\n { key: 'here', title: 'On Yantra Chat', data: onYantra.filter(filterContact) },\n { key: 'invite', title: 'Invite to Yantra Chat', data: invitable.filter(filterContact) },\n ].filter((s) => s.data.length > 0),\n [onYantra, invitable, filterContact],\n );\n\n const hasPeople = onYantra.length > 0 || invitable.length > 0;\n\n const shell = (body: React.ReactNode) => (\n <View style={[styles.fill, { backgroundColor: theme.bg }]}>\n <ContactsHeader theme={theme} onBack={onBack} />\n {permission === 'granted' ? (\n <View style={[styles.searchWrap, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name=\"magnify\" size={20} color={theme.textMuted} />\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search name or number\"\n placeholderTextColor={theme.textMuted}\n style={[styles.searchInput, { color: theme.text }]}\n />\n </View>\n ) : null}\n {body}\n </View>\n );\n\n if (permission === 'unsupported' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"cellphone-off\"\n title=\"Not available in this build\"\n detail=\"Finding people from your contacts needs a newer version of the app.\"\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>,\n );\n }\n\n if (permission === 'denied' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"lock-outline\"\n title=\"Contacts access is off\"\n detail=\"Allow contacts access in Settings to find people you know on Yantra Chat.\"\n theme={theme}\n action={{ label: 'Open Settings', onPress: onOpenSettings }}\n />\n </View>,\n );\n }\n\n if (permission === 'undetermined' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-multiple-outline\"\n title={loading ? 'Looking for people' : 'No other members yet'}\n detail={\n loading\n ? 'Checking who is already on Yantra Chat…'\n : 'You are registered. Allow contacts to invite people, or start a chat when someone else registers.'\n }\n theme={theme}\n action={{ label: 'Find from contacts', onPress: onRequestAccess }}\n />\n </View>,\n );\n }\n\n if (loading && !sections.length) {\n return shell(\n <View style={styles.centerFill}>\n <ActivityIndicator color={theme.accent} />\n <Text style={[styles.loadingText, { color: theme.textMuted }]}>Checking your contacts…</Text>\n </View>,\n );\n }\n\n return shell(\n <>\n <InlineBanner message={error} theme={theme} onDismiss={onDismissError} />\n <SectionList\n sections={sections}\n keyExtractor={(item) => item.id}\n stickySectionHeadersEnabled={false}\n renderSectionHeader={({ section }) => (\n <Text style={[styles.sectionTitle, { color: theme.textMuted, backgroundColor: theme.bg }]}>\n {section.title}\n </Text>\n )}\n renderItem={({ item, section }) => (\n <ContactRow\n contact={item}\n theme={theme}\n busy={busyContactId === item.id}\n mode={section.key === 'here' ? 'chat' : 'invite'}\n onPress={() => (section.key === 'here' ? onStartChat(item) : onInvite(item))}\n />\n )}\n ListEmptyComponent={\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-search-outline\"\n title=\"No contacts found\"\n detail=\"Add phone numbers to your contacts, or search with a different name.\"\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>\n }\n contentContainerStyle={sections.length === 0 ? styles.listEmptyGrow : undefined}\n testID=\"chat-contacts-list\"\n />\n </>,\n );\n}\n\nfunction ContactRow({\n contact,\n theme,\n mode,\n busy,\n onPress,\n}: {\n contact: MatchedContact;\n theme: ChatTheme;\n mode: 'chat' | 'invite';\n busy: boolean;\n onPress: () => void;\n}) {\n const name = contact.match?.displayName || contact.displayName;\n return (\n <Pressable\n onPress={onPress}\n disabled={busy}\n accessibilityRole=\"button\"\n accessibilityLabel={mode === 'chat' ? `Message ${name}` : `Invite ${name}`}\n style={({ pressed }) => ({\n width: '100%',\n backgroundColor: pressed ? theme.surface : theme.bg,\n })}\n testID={`chat-contact-${contact.id}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.row}>\n <Avatar name={name} uri={contact.match?.picture ?? contact.imageUri} theme={theme} size={44} />\n <View style={[styles.rowBody, { borderBottomColor: theme.border }]}>\n <View style={styles.rowText}>\n <Text numberOfLines={1} style={[styles.rowName, { color: theme.text }]}>\n {name}\n </Text>\n <Text numberOfLines={1} style={[styles.rowPhone, { color: theme.textMuted }]}>\n {contact.rawNumbers?.[0] ?? contact.e164[0]}\n </Text>\n </View>\n {busy ? (\n <ActivityIndicator size=\"small\" color={theme.accent} />\n ) : mode === 'invite' ? (\n <View style={[styles.inviteChip, { borderColor: theme.accent }]}>\n <Text style={[styles.inviteText, { color: theme.accent }]}>Invite</Text>\n </View>\n ) : null}\n </View>\n </View>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n centerFill: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 28,\n minHeight: 360,\n },\n listEmptyGrow: { flexGrow: 1 },\n loadingText: { fontSize: 14, marginTop: 12 },\n searchWrap: {\n flexDirection: 'row',\n alignItems: 'center',\n marginHorizontal: 16,\n marginVertical: 8,\n borderRadius: 10,\n paddingHorizontal: 12,\n height: 36,\n },\n searchInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n row: { flexDirection: 'row', alignItems: 'center', width: '100%', paddingLeft: 16 },\n rowBody: {\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n marginLeft: 12,\n paddingVertical: 12,\n paddingRight: 16,\n minHeight: 64,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n rowText: { flex: 1, minWidth: 0, marginRight: 12 },\n rowName: { fontSize: 16, fontWeight: '600' },\n rowPhone: { fontSize: 13, marginTop: 2 },\n inviteChip: { borderWidth: 1, borderRadius: 14, paddingHorizontal: 12, paddingVertical: 4 },\n inviteText: { fontSize: 13, fontWeight: '600' },\n sectionTitle: {\n fontSize: 13,\n fontWeight: '600',\n paddingHorizontal: 16,\n paddingTop: 14,\n paddingBottom: 6,\n },\n});\n"],"names":[],"mappings":"mWAUA,SAAS,cAAe,CAAA;AAAA,EACtB,KAAA;AAAA,EACA;AACF,CAGG,EAAA;AACD,EAAA,2BAAQ,YAAa,EAAA,EAAA,KAAA,EAAO,CAAC,KAAK,GAAG,KAAO,EAAA;AAAA,IAC1C,iBAAiB,KAAM,CAAA;AAAA,GACzB,EACU,QAAC,kBAAA,IAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA;AAAA,IACnB,aAAe,EAAA,KAAA;AAAA,IACf,UAAY,EAAA,QAAA;AAAA,IACZ,iBAAmB,EAAA,CAAA;AAAA,IACnB,aAAe,EAAA,CAAA;AAAA,IACf,mBAAmB,UAAW,CAAA,aAAA;AAAA,IAC9B,mBAAmB,KAAM,CAAA,MAAA;AAAA,IACzB,iBAAiB,KAAM,CAAA;AAAA,GAEb,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,aAAU,OAAS,EAAA,MAAA,EAAQ,SAAS,EAAI,EAAA,iBAAA,EAAkB,UAAS,kBAAmB,EAAA,MAAA,EACnF,QAAC,kBAAA,GAAA,CAAA,sBAAA,EAAA,EAAuB,MAAK,cAAe,EAAA,IAAA,EAAM,IAAI,KAAO,EAAA,KAAA,CAAM,QAAQ,CAC/E,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,QAAK,KAAO,EAAA;AAAA,MACrB,IAAM,EAAA,CAAA;AAAA,MACN,QAAU,EAAA,EAAA;AAAA,MACV,UAAY,EAAA,KAAA;AAAA,MACZ,OAAO,KAAM,CAAA,IAAA;AAAA,MACb,UAAY,EAAA;AAAA,OACX,QAEO,EAAA,gBAAA,EAAA;AAAA,GAAA,EACJ,CACJ,EAAA,CAAA;AACR;AACO,SAAS,cAAe,CAAA;AAAA,EAC7B,UAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAcG,EAAA;AACD,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,CAAI,GAAA,KAAA,CAAM,SAAS,EAAE,CAAA;AAC3C,EAAA,MAAM,aAAgB,GAAA,KAAA,CAAM,WAAY,CAAA,CAAC,CAAsB,KAAA;AA1EjE,IAAA,IAAA,EAAA,EAAA,EAAA;AA2EI,IAAA,MAAM,CAAI,GAAA,KAAA,CAAM,IAAK,EAAA,CAAE,WAAY,EAAA;AACnC,IAAI,IAAA,CAAC,GAAU,OAAA,IAAA;AACf,IAAA,MAAM,UAAQ,EAAE,GAAA,CAAA,CAAA,KAAA,KAAF,mBAAS,WAAe,KAAA,CAAA,CAAE,aAAa,WAAY,EAAA;AACjE,IAAA,MAAM,OAAU,GAAA,CAAA,CAAE,OAAQ,CAAA,KAAA,EAAO,EAAE,CAAA;AACnC,IAAO,OAAA,IAAA,CAAK,QAAS,CAAA,CAAC,CAAK,IAAA,OAAA,CAAQ,MAAS,GAAA,CAAA,IAAK,CAAC,GAAG,CAAE,CAAA,IAAA,EAAM,GAAI,CAAA,EAAA,GAAA,CAAA,CAAE,UAAF,KAAA,IAAA,GAAA,EAAA,GAAgB,EAAG,CAAE,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,CAAE,OAAQ,CAAA,KAAA,EAAO,EAAE,CAAA,CAAE,QAAS,CAAA,OAAO,CAAC,CAAA;AAAA,GACxI,EAAG,CAAC,KAAK,CAAC,CAAA;AACV,EAAA,MAAM,QAAW,GAAA,KAAA,CAAM,OAAQ,CAAA,MAAM,CAAC;AAAA,IACpC,GAAK,EAAA,MAAA;AAAA,IACL,KAAO,EAAA,gBAAA;AAAA,IACP,IAAA,EAAM,QAAS,CAAA,MAAA,CAAO,aAAa;AAAA,GAClC,EAAA;AAAA,IACD,GAAK,EAAA,QAAA;AAAA,IACL,KAAO,EAAA,uBAAA;AAAA,IACP,IAAA,EAAM,SAAU,CAAA,MAAA,CAAO,aAAa;AAAA,GACrC,CAAA,CAAE,MAAO,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,IAAA,CAAK,MAAS,GAAA,CAAC,CAAG,EAAA,CAAC,QAAU,EAAA,SAAA,EAAW,aAAa,CAAC,CAAA;AACvE,EAAA,MAAM,SAAY,GAAA,QAAA,CAAS,MAAS,GAAA,CAAA,IAAK,UAAU,MAAS,GAAA,CAAA;AAC5D,EAAM,MAAA,KAAA,GAAQ,CAAC,IAA0B,qBAAA,IAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,IAAM,EAAA;AAAA,IAClE,iBAAiB,KAAM,CAAA;AAAA,GACxB,CACS,EAAA,QAAA,EAAA;AAAA,oBAAC,GAAA,CAAA,cAAA,EAAA,EAAe,OAAc,MAAgB,EAAA,CAAA;AAAA,IAC7C,eAAe,SAAY,mBAAA,IAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,UAAY,EAAA;AAAA,MACnE,iBAAiB,KAAM,CAAA;AAAA,KACxB,CACe,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,0BAAuB,IAAK,EAAA,SAAA,EAAU,MAAM,EAAI,EAAA,KAAA,EAAO,MAAM,SAAW,EAAA,CAAA;AAAA,sBACxE,GAAA,CAAA,SAAA,EAAA,EAAU,KAAO,EAAA,KAAA,EAAO,cAAc,QAAU,EAAA,WAAA,EAAY,uBAAwB,EAAA,oBAAA,EAAsB,KAAM,CAAA,SAAA,EAAW,KAAO,EAAA,CAAC,OAAO,WAAa,EAAA;AAAA,QACpK,OAAO,KAAM,CAAA;AAAA,OACd,CAAG,EAAA;AAAA,KAAA,EACM,CAAU,GAAA,IAAA;AAAA,IACb;AAAA,GACL,EAAA,CAAA;AACN,EAAI,IAAA,UAAA,KAAe,aAAiB,IAAA,CAAC,SAAW,EAAA;AAC9C,IAAA,OAAO,KAAM,iBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,OAAO,UACrB,EAAA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,IAAA,EAAK,iBAAgB,KAAM,EAAA,6BAAA,EAA8B,MAAO,EAAA,qEAAA,EAAsE,OAAc,MAAQ,EAAA;AAAA,MAChL,KAAO,EAAA,eAAA;AAAA,MACP,OAAS,EAAA;AAAA,KACX,EAAG,GACG,CAAO,CAAA;AAAA;AAEjB,EAAI,IAAA,UAAA,KAAe,QAAY,IAAA,CAAC,SAAW,EAAA;AACzC,IAAA,OAAO,KAAM,iBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,OAAO,UACrB,EAAA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,IAAA,EAAK,gBAAe,KAAM,EAAA,wBAAA,EAAyB,MAAO,EAAA,2EAAA,EAA4E,OAAc,MAAQ,EAAA;AAAA,MAChL,KAAO,EAAA,eAAA;AAAA,MACP,OAAS,EAAA;AAAA,KACX,EAAG,GACG,CAAO,CAAA;AAAA;AAEjB,EAAI,IAAA,UAAA,KAAe,cAAkB,IAAA,CAAC,SAAW,EAAA;AAC/C,IAAA,OAAO,sBAAO,GAAA,CAAA,IAAA,EAAA,EAAK,OAAO,MAAO,CAAA,UAAA,EACrB,8BAAC,UAAW,EAAA,EAAA,IAAA,EAAK,4BAA2B,KAAO,EAAA,OAAA,GAAU,uBAAuB,sBAAwB,EAAA,MAAA,EAAQ,UAAU,8CAA4C,GAAA,mGAAA,EAAqG,OAAc,MAAQ,EAAA;AAAA,MAC7S,KAAO,EAAA,oBAAA;AAAA,MACP,OAAS,EAAA;AAAA,KACX,EAAG,GACG,CAAO,CAAA;AAAA;AAEjB,EAAI,IAAA,OAAA,IAAW,CAAC,QAAA,CAAS,MAAQ,EAAA;AAC/B,IAAA,OAAO,KAAM,iBAAA,IAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,OAAO,UACrB,EAAA,QAAA,EAAA;AAAA,sBAAC,GAAA,CAAA,iBAAA,EAAA,EAAkB,KAAO,EAAA,KAAA,CAAM,MAAQ,EAAA,CAAA;AAAA,sBACvC,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,WAAa,EAAA;AAAA,QAC1C,OAAO,KAAM,CAAA;AAAA,OACd,GAAG,QAAuB,EAAA,8BAAA,EAAA;AAAA,KAAA,EACrB,CAAO,CAAA;AAAA;AAEjB,EAAA,OAAO,sBACG,IAAA,CAAA,QAAA,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,YAAa,EAAA,EAAA,OAAA,EAAS,KAAO,EAAA,KAAA,EAAc,WAAW,cAAgB,EAAA,CAAA;AAAA,oBACvE,GAAA,CAAC,WAAY,EAAA,EAAA,QAAA,EAAoB,YAAc,EAAA,CAAA,IAAA,KAAQ,KAAK,EAAI,EAAA,2BAAA,EAA6B,KAAO,EAAA,mBAAA,EAAqB,CAAC;AAAA,MAChI;AAAA,0BACK,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,YAAc,EAAA;AAAA,MACvC,OAAO,KAAM,CAAA,SAAA;AAAA,MACb,iBAAiB,KAAM,CAAA;AAAA,KACxB,CACoB,EAAA,QAAA,EAAA,OAAA,CAAQ,KACb,EAAA,CAAA,EAAS,YAAY,CAAC;AAAA,MACpC,IAAA;AAAA,MACA;AAAA,0BACK,GAAA,CAAA,UAAA,EAAA,EAAW,SAAS,IAAM,EAAA,KAAA,EAAc,MAAM,aAAkB,KAAA,IAAA,CAAK,IAAI,IAAM,EAAA,OAAA,CAAQ,QAAQ,MAAS,GAAA,MAAA,GAAS,UAAU,OAAS,EAAA,MAAM,QAAQ,GAAQ,KAAA,MAAA,GAAS,YAAY,IAAI,CAAA,GAAI,SAAS,IAAI,CAAA,EAAG,GAAI,kBAAoB,kBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,MAAA,CAAO,YACtO,QAAC,kBAAA,GAAA,CAAA,UAAA,EAAA,EAAW,MAAK,wBAAyB,EAAA,KAAA,EAAM,qBAAoB,MAAO,EAAA,sEAAA,EAAuE,OAAc,MAAQ,EAAA;AAAA,MACxL,KAAO,EAAA,eAAA;AAAA,MACP,OAAS,EAAA;AAAA,KACX,EAAG,CACW,EAAA,CAAA,EAAS,qBAAuB,EAAA,QAAA,CAAS,MAAW,KAAA,CAAA,GAAI,MAAO,CAAA,aAAA,GAAgB,MAAW,EAAA,MAAA,EAAO,oBAAqB,EAAA;AAAA,GAAA,EAClI,CAAG,CAAA;AACX;AACA,SAAS,UAAW,CAAA;AAAA,EAClB,OAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAMG,EAAA;AAzKH,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA0KE,EAAA,MAAM,IAAO,GAAA,CAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,KAAR,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,gBAAe,OAAQ,CAAA,WAAA;AACnD,EAAA,2BAAQ,SAAU,EAAA,EAAA,OAAA,EAAkB,QAAU,EAAA,IAAA,EAAM,mBAAkB,QAAS,EAAA,kBAAA,EAAoB,IAAS,KAAA,MAAA,GAAS,WAAW,IAAI,CAAA,CAAA,GAAK,UAAU,IAAI,CAAA,CAAA,EAAI,OAAO,CAAC;AAAA,IACjK;AAAA,GACK,MAAA;AAAA,IACL,KAAO,EAAA,MAAA;AAAA,IACP,eAAiB,EAAA,OAAA,GAAU,KAAM,CAAA,OAAA,GAAU,KAAM,CAAA;AAAA,GACnD,CAAA,EAAI,MAAQ,EAAA,CAAA,aAAA,EAAgB,OAAQ,CAAA,EAAE,IAE5B,QAAC,kBAAA,IAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,MAAA,CAAO,GAChB,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAO,EAAA,EAAA,IAAA,EAAY,GAAK,EAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,KAAR,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,OAAf,KAAA,IAAA,GAAA,EAAA,GAA0B,OAAQ,CAAA,QAAA,EAAU,KAAc,EAAA,IAAA,EAAM,EAAI,EAAA,CAAA;AAAA,oBAC5F,IAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,OAAS,EAAA;AAAA,MACtC,mBAAmB,KAAM,CAAA;AAAA,KAC1B,CACa,EAAA,QAAA,EAAA;AAAA,sBAAC,IAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,MAAA,CAAO,OAChB,EAAA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,QAAK,aAAe,EAAA,CAAA,EAAG,KAAO,EAAA,CAAC,OAAO,OAAS,EAAA;AAAA,UAC5D,OAAO,KAAM,CAAA;AAAA,SACd,GACkB,QACL,EAAA,IAAA,EAAA,CAAA;AAAA,4BACC,IAAK,EAAA,EAAA,aAAA,EAAe,GAAG,KAAO,EAAA,CAAC,OAAO,QAAU,EAAA;AAAA,UAC7D,OAAO,KAAM,CAAA;AAAA,SACd,CACkB,EAAA,QAAA,EAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,UAAR,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAqB,OAArB,IAA2B,GAAA,EAAA,GAAA,OAAA,CAAQ,IAAK,CAAA,CAAC,CAC9C,EAAA;AAAA,OACJ,EAAA,CAAA;AAAA,MACC,uBAAQ,GAAA,CAAA,iBAAA,EAAA,EAAkB,IAAK,EAAA,OAAA,EAAQ,OAAO,KAAM,CAAA,MAAA,EAAQ,CAAK,GAAA,IAAA,KAAS,2BAAY,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,UAAY,EAAA;AAAA,QACjI,aAAa,KAAM,CAAA;AAAA,OACpB,CACmB,EAAA,QAAA,kBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,UAAY,EAAA;AAAA,QACjD,OAAO,KAAM,CAAA;AAAA,OACd,CAAA,EAAG,QAAM,EAAA,QAAA,EAAA,CAAA,EACI,CAAU,GAAA;AAAA,KAClB,EAAA;AAAA,GAAA,EACJ,CACJ,EAAA,CAAA;AACR;AACA,MAAM,MAAA,GAAS,WAAW,MAAO,CAAA;AAAA,EAC/B,IAAM,EAAA;AAAA,IACJ,IAAM,EAAA;AAAA,GACR;AAAA,EACA,UAAY,EAAA;AAAA,IACV,IAAM,EAAA,CAAA;AAAA,IACN,UAAY,EAAA,QAAA;AAAA,IACZ,cAAgB,EAAA,QAAA;AAAA,IAChB,iBAAmB,EAAA,EAAA;AAAA,IACnB,SAAW,EAAA;AAAA,GACb;AAAA,EACA,aAAe,EAAA;AAAA,IACb,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,QAAU,EAAA,EAAA;AAAA,IACV,SAAW,EAAA;AAAA,GACb;AAAA,EACA,UAAY,EAAA;AAAA,IACV,aAAe,EAAA,KAAA;AAAA,IACf,UAAY,EAAA,QAAA;AAAA,IACZ,gBAAkB,EAAA,EAAA;AAAA,IAClB,cAAgB,EAAA,CAAA;AAAA,IAChB,YAAc,EAAA,EAAA;AAAA,IACd,iBAAmB,EAAA,EAAA;AAAA,IACnB,MAAQ,EAAA;AAAA,GACV;AAAA,EACA,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,CAAA;AAAA,IACN,UAAY,EAAA,CAAA;AAAA,IACZ,QAAU,EAAA,EAAA;AAAA,IACV,eAAiB,EAAA;AAAA,GACnB;AAAA,EACA,GAAK,EAAA;AAAA,IACH,aAAe,EAAA,KAAA;AAAA,IACf,UAAY,EAAA,QAAA;AAAA,IACZ,KAAO,EAAA,MAAA;AAAA,IACP,WAAa,EAAA;AAAA,GACf;AAAA,EACA,OAAS,EAAA;AAAA,IACP,IAAM,EAAA,CAAA;AAAA,IACN,aAAe,EAAA,KAAA;AAAA,IACf,UAAY,EAAA,QAAA;AAAA,IACZ,UAAY,EAAA,EAAA;AAAA,IACZ,eAAiB,EAAA,EAAA;AAAA,IACjB,YAAc,EAAA,EAAA;AAAA,IACd,SAAW,EAAA,EAAA;AAAA,IACX,mBAAmB,UAAW,CAAA;AAAA,GAChC;AAAA,EACA,OAAS,EAAA;AAAA,IACP,IAAM,EAAA,CAAA;AAAA,IACN,QAAU,EAAA,CAAA;AAAA,IACV,WAAa,EAAA;AAAA,GACf;AAAA,EACA,OAAS,EAAA;AAAA,IACP,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,QAAU,EAAA;AAAA,IACR,QAAU,EAAA,EAAA;AAAA,IACV,SAAW,EAAA;AAAA,GACb;AAAA,EACA,UAAY,EAAA;AAAA,IACV,WAAa,EAAA,CAAA;AAAA,IACb,YAAc,EAAA,EAAA;AAAA,IACd,iBAAmB,EAAA,EAAA;AAAA,IACnB,eAAiB,EAAA;AAAA,GACnB;AAAA,EACA,UAAY,EAAA;AAAA,IACV,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA,KAAA;AAAA,IACZ,iBAAmB,EAAA,EAAA;AAAA,IACnB,UAAY,EAAA,EAAA;AAAA,IACZ,aAAe,EAAA;AAAA;AAEnB,CAAC,CAAA"}
1
+ {"version":3,"file":"ContactsScreen.js","sources":["../../../src/features/messaging/ContactsScreen.tsx"],"sourcesContent":["/**\n * \"Find people you know\" - the contacts sync and invite screen.\n */\nimport * as React from 'react';\nimport { ActivityIndicator, Pressable, SectionList, StyleSheet, Text, TextInput, View } from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Avatar, EmptyState, InlineBanner, type ChatTheme } from './ChatComponents';\nimport type { MatchedContact } from './useContactMatches';\nimport type { ChatPermissionStatus } from './chatHost';\n\nfunction ContactsHeader({ theme, onBack }: { theme: ChatTheme; onBack: () => void }) {\n return (\n <SafeAreaView edges={['top']} style={{ backgroundColor: theme.headerBg }}>\n <View\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 8,\n paddingBottom: 8,\n borderBottomWidth: StyleSheet.hairlineWidth,\n borderBottomColor: theme.border,\n backgroundColor: theme.headerBg,\n }}\n >\n <Pressable onPress={onBack} hitSlop={10} accessibilityRole=\"button\" accessibilityLabel=\"Back\">\n <MaterialCommunityIcons name=\"chevron-left\" size={28} color={theme.accent} />\n </Pressable>\n <Text\n style={{\n flex: 1,\n fontSize: 20,\n fontWeight: '700',\n color: theme.text,\n marginLeft: 8,\n }}\n >\n Select contact\n </Text>\n </View>\n </SafeAreaView>\n );\n}\n\nexport function ContactsScreen({\n permission,\n loading,\n error,\n onYantra,\n invitable,\n theme,\n busyContactId,\n onBack,\n onRequestAccess,\n onOpenSettings,\n onStartChat,\n onInvite,\n onDismissError,\n limited = false,\n onChooseMoreContacts,\n}: {\n permission: ChatPermissionStatus | 'unsupported';\n loading: boolean;\n error: string | null;\n onYantra: MatchedContact[];\n invitable: MatchedContact[];\n theme: ChatTheme;\n busyContactId: string | null;\n onBack: () => void;\n onRequestAccess: () => void;\n onOpenSettings: () => void;\n onStartChat: (contact: MatchedContact) => void;\n onInvite: (contact: MatchedContact) => void;\n onDismissError: () => void;\n /** Only some contacts are shared (iOS 18 Limited Access). */\n limited?: boolean;\n onChooseMoreContacts?: () => void;\n}) {\n const [query, setQuery] = React.useState('');\n\n const filterContact = React.useCallback(\n (c: MatchedContact) => {\n const q = query.trim().toLowerCase();\n if (!q) return true;\n const name = (c.match?.displayName || c.displayName).toLowerCase();\n const qDigits = q.replace(/\\D/g, '');\n return (\n name.includes(q) ||\n (qDigits.length > 0 &&\n [...c.e164, ...(c.rawNumbers ?? [])].some((n) => n.replace(/\\D/g, '').includes(qDigits)))\n );\n },\n [query],\n );\n\n const sections = React.useMemo(\n () =>\n [\n { key: 'here', title: 'On Yantra Chat', data: onYantra.filter(filterContact) },\n { key: 'invite', title: 'Invite to Yantra Chat', data: invitable.filter(filterContact) },\n ].filter((s) => s.data.length > 0),\n [onYantra, invitable, filterContact],\n );\n\n const hasPeople = onYantra.length > 0 || invitable.length > 0;\n\n const shell = (body: React.ReactNode) => (\n <View style={[styles.fill, { backgroundColor: theme.bg }]}>\n <ContactsHeader theme={theme} onBack={onBack} />\n {permission === 'granted' ? (\n <View style={[styles.searchWrap, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name=\"magnify\" size={20} color={theme.textMuted} />\n <TextInput\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search name or number\"\n placeholderTextColor={theme.textMuted}\n style={[styles.searchInput, { color: theme.text }]}\n />\n </View>\n ) : null}\n {body}\n </View>\n );\n\n if (permission === 'unsupported' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"cellphone-off\"\n title=\"Not available in this build\"\n detail=\"Finding people from your contacts needs a newer version of the app.\"\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>,\n );\n }\n\n if (permission === 'denied' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"lock-outline\"\n title=\"Contacts access is off\"\n detail=\"Allow contacts access in Settings to find people you know on Yantra Chat.\"\n theme={theme}\n action={{ label: 'Open Settings', onPress: onOpenSettings }}\n />\n </View>,\n );\n }\n\n if (permission === 'undetermined' && !hasPeople) {\n return shell(\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-multiple-outline\"\n title={loading ? 'Looking for people' : 'No other members yet'}\n detail={\n loading\n ? 'Checking who is already on Yantra Chat…'\n : 'You are registered. Allow contacts to invite people, or start a chat when someone else registers.'\n }\n theme={theme}\n action={{ label: 'Find from contacts', onPress: onRequestAccess }}\n />\n </View>,\n );\n }\n\n if (loading && !sections.length) {\n return shell(\n <View style={styles.centerFill}>\n <ActivityIndicator color={theme.accent} />\n <Text style={[styles.loadingText, { color: theme.textMuted }]}>Checking your contacts…</Text>\n </View>,\n );\n }\n\n // A limited selection is never a dead end: the user can share more contacts from here.\n const listHeader =\n permission === 'granted' && limited ? (\n <View>\n <View style={[styles.limitedNote, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name=\"account-lock-outline\" size={20} color={theme.textMuted} />\n <Text style={[styles.limitedText, { color: theme.textMuted }]}>\n You shared only some contacts with Yantra. People you did not share will not show here.\n </Text>\n </View>\n {onChooseMoreContacts ? (\n <ActionRow\n icon=\"account-multiple-plus-outline\"\n label=\"Choose more contacts\"\n theme={theme}\n onPress={onChooseMoreContacts}\n testID=\"chat-contacts-choose-more\"\n />\n ) : null}\n </View>\n ) : null;\n\n return shell(\n <>\n <InlineBanner message={error} theme={theme} onDismiss={onDismissError} />\n <SectionList\n ListHeaderComponent={listHeader}\n sections={sections}\n keyExtractor={(item) => item.id}\n stickySectionHeadersEnabled={false}\n renderSectionHeader={({ section }) => (\n <Text style={[styles.sectionTitle, { color: theme.textMuted, backgroundColor: theme.bg }]}>\n {section.title}\n </Text>\n )}\n renderItem={({ item, section }) => (\n <ContactRow\n contact={item}\n theme={theme}\n busy={busyContactId === item.id}\n mode={section.key === 'here' ? 'chat' : 'invite'}\n onPress={() => (section.key === 'here' ? onStartChat(item) : onInvite(item))}\n />\n )}\n ListEmptyComponent={\n <View style={styles.centerFill}>\n <EmptyState\n icon=\"account-search-outline\"\n title=\"No contacts found\"\n detail={\n limited && !query.trim()\n ? 'Choose more contacts to share with Yantra.'\n : 'Add phone numbers to your contacts, or search with a different name.'\n }\n theme={theme}\n action={{ label: 'Back to chats', onPress: onBack }}\n />\n </View>\n }\n contentContainerStyle={sections.length === 0 ? styles.listEmptyGrow : undefined}\n testID=\"chat-contacts-list\"\n />\n </>,\n );\n}\n\nfunction ActionRow({\n icon,\n label,\n theme,\n onPress,\n testID,\n}: {\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n label: string;\n theme: ChatTheme;\n onPress: () => void;\n testID: string;\n}) {\n return (\n <Pressable\n onPress={onPress}\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n style={({ pressed }) => ({ width: '100%', backgroundColor: pressed ? theme.surface : theme.bg })}\n testID={testID}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.row}>\n <View style={[styles.actionIcon, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name={icon} size={22} color={theme.accent} />\n </View>\n <View style={[styles.rowBody, { borderBottomColor: theme.border }]}>\n <Text style={[styles.rowName, { color: theme.accent }]}>{label}</Text>\n </View>\n </View>\n </Pressable>\n );\n}\n\nfunction ContactRow({\n contact,\n theme,\n mode,\n busy,\n onPress,\n}: {\n contact: MatchedContact;\n theme: ChatTheme;\n mode: 'chat' | 'invite';\n busy: boolean;\n onPress: () => void;\n}) {\n const name = contact.match?.displayName || contact.displayName;\n return (\n <Pressable\n onPress={onPress}\n disabled={busy}\n accessibilityRole=\"button\"\n accessibilityLabel={mode === 'chat' ? `Message ${name}` : `Invite ${name}`}\n style={({ pressed }) => ({\n width: '100%',\n backgroundColor: pressed ? theme.surface : theme.bg,\n })}\n testID={`chat-contact-${contact.id}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.row}>\n <Avatar name={name} uri={contact.match?.picture ?? contact.imageUri} theme={theme} size={44} />\n <View style={[styles.rowBody, { borderBottomColor: theme.border }]}>\n <View style={styles.rowText}>\n <Text numberOfLines={1} style={[styles.rowName, { color: theme.text }]}>\n {name}\n </Text>\n <Text numberOfLines={1} style={[styles.rowPhone, { color: theme.textMuted }]}>\n {contact.rawNumbers?.[0] ?? contact.e164[0]}\n </Text>\n </View>\n {busy ? (\n <ActivityIndicator size=\"small\" color={theme.accent} />\n ) : mode === 'invite' ? (\n <View style={[styles.inviteChip, { borderColor: theme.accent }]}>\n <Text style={[styles.inviteText, { color: theme.accent }]}>Invite</Text>\n </View>\n ) : null}\n </View>\n </View>\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n centerFill: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n paddingHorizontal: 28,\n minHeight: 360,\n },\n listEmptyGrow: { flexGrow: 1 },\n loadingText: { fontSize: 14, marginTop: 12 },\n searchWrap: {\n flexDirection: 'row',\n alignItems: 'center',\n marginHorizontal: 16,\n marginVertical: 8,\n borderRadius: 10,\n paddingHorizontal: 12,\n height: 36,\n },\n searchInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n row: { flexDirection: 'row', alignItems: 'center', width: '100%', paddingLeft: 16 },\n rowBody: {\n flex: 1,\n flexDirection: 'row',\n alignItems: 'center',\n marginLeft: 12,\n paddingVertical: 12,\n paddingRight: 16,\n minHeight: 64,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n rowText: { flex: 1, minWidth: 0, marginRight: 12 },\n rowName: { fontSize: 16, fontWeight: '600' },\n rowPhone: { fontSize: 13, marginTop: 2 },\n actionIcon: { width: 44, height: 44, borderRadius: 22, alignItems: 'center', justifyContent: 'center' },\n limitedNote: {\n flexDirection: 'row',\n alignItems: 'flex-start',\n marginHorizontal: 16,\n marginTop: 4,\n marginBottom: 4,\n padding: 12,\n borderRadius: 10,\n },\n limitedText: { flex: 1, fontSize: 13, lineHeight: 18, marginLeft: 10 },\n inviteChip: { borderWidth: 1, borderRadius: 14, paddingHorizontal: 12, paddingVertical: 4 },\n inviteText: { fontSize: 13, fontWeight: '600' },\n sectionTitle: {\n fontSize: 13,\n fontWeight: '600',\n paddingHorizontal: 16,\n paddingTop: 14,\n paddingBottom: 6,\n },\n});\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import {jsx,jsxs,Fragment}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,Dimensions,Modal,Pressable,View,Text,KeyboardAvoidingView,TextInput,ScrollView,Platform}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {emojiFromInput,EMOJI_CATEGORIES}from'./emojiCatalog.js';var __defProp = Object.defineProperty;
1
+ import {jsx,jsxs,Fragment}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,Dimensions,Modal,Pressable,View,Text,KeyboardAvoidingView,TextInput,ScrollView,Platform}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {searchEmoji,EMOJI_CATEGORIES,emojiFromInput}from'./emojiCatalog.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -117,6 +117,13 @@ function EmojiPickerSheet({
117
117
  }) {
118
118
  const [text, setText] = React.useState("");
119
119
  const cell = Math.floor((Dimensions.get("window").width - 24) / 8);
120
+ const query = text.trim();
121
+ const results = React.useMemo(() => query ? searchEmoji(query) : null, [query]);
122
+ const sections = results ? [{
123
+ key: "results",
124
+ title: results.length ? "Results" : "No emoji found",
125
+ emojis: results
126
+ }] : EMOJI_CATEGORIES;
120
127
  return /* @__PURE__ */ jsxs(Fragment, { children: [
121
128
  /* @__PURE__ */ jsx(Pressable, { style: styles.sheetBackdrop, onPress: onClose, accessibilityLabel: "Close emoji picker" }),
122
129
  /* @__PURE__ */ jsx(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : void 0, style: styles.sheetDock, children: /* @__PURE__ */ jsxs(View, { style: [styles.sheet, {
@@ -138,11 +145,11 @@ function EmojiPickerSheet({
138
145
  return;
139
146
  }
140
147
  setText(value);
141
- }, placeholder: "Type any emoji (\u{1F310} for the emoji keyboard)", placeholderTextColor: theme.textMuted, style: [styles.sheetInput, {
148
+ }, placeholder: "Search emoji", placeholderTextColor: theme.textMuted, style: [styles.sheetInput, {
142
149
  color: theme.text
143
- }], autoCorrect: false, testID: "chat-emoji-input" })
150
+ }], autoCorrect: false, autoCapitalize: "none", returnKeyType: "search", clearButtonMode: "while-editing", testID: "chat-emoji-input" })
144
151
  ] }),
145
- /* @__PURE__ */ jsx(ScrollView, { style: styles.sheetScroll, keyboardShouldPersistTaps: "handled", children: EMOJI_CATEGORIES.map((category) => /* @__PURE__ */ jsxs(View, { children: [
152
+ /* @__PURE__ */ jsx(ScrollView, { style: styles.sheetScroll, keyboardShouldPersistTaps: "handled", children: sections.map((category) => /* @__PURE__ */ jsxs(View, { children: [
146
153
  /* @__PURE__ */ jsx(Text, { style: [styles.sheetTitle, {
147
154
  color: theme.textMuted
148
155
  }], children: category.title }),
@@ -1 +1 @@
1
- {"version":3,"file":"MessageActions.js","sources":["../../../src/features/messaging/MessageActions.tsx"],"sourcesContent":["/**\n * iMessage-style long-press on a message: the Tapback bar above the bubble and\n * an action menu below it, over a dimmed transcript.\n *\n * Plain react-native `Modal` on purpose - the chat remote may only use what the\n * host shares, so no blur view or gesture library.\n */\nimport * as React from 'react';\nimport {\n Dimensions,\n KeyboardAvoidingView,\n Modal,\n Platform,\n Pressable,\n ScrollView,\n StyleSheet,\n Text,\n TextInput,\n View,\n} from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport type { ChatTheme } from './ChatComponents';\nimport type { ChatMessage } from './chatModel';\nimport { EMOJI_CATEGORIES, emojiFromInput } from './emojiCatalog';\n\n/** The iMessage Tapback set. */\nexport const TAPBACKS = ['❤️', '👍', '👎', '😂', '‼️', '❓'] as const;\n\nexport interface BubbleFrame {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface MessageAction {\n key: string;\n label: string;\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n onPress: () => void;\n}\n\nconst BAR_HEIGHT = 52;\nconst GAP = 8;\nconst MENU_ROW = 46;\nconst EDGE = 12;\n\nexport function MessageActionsOverlay({\n message,\n frame,\n theme,\n actions,\n onReact,\n onClose,\n onDismissed,\n}: {\n message: ChatMessage | null;\n frame: BubbleFrame | null;\n theme: ChatTheme;\n actions: MessageAction[];\n onReact: (emoji: string) => void;\n onClose: () => void;\n /** Fires once the overlay is fully gone (iOS `onDismiss`), for work that needs to present UI. */\n onDismissed?: () => void;\n}) {\n const visible = Boolean(message && frame);\n const { height: screenH, width: screenW } = Dimensions.get('window');\n const mine = Boolean(message?.isMine);\n const current = message?.reactions.find((r) => r.mine)?.reaction ?? null;\n const [picking, setPicking] = React.useState(false);\n // A custom (non-Tapback) pick sits in the bar before \"+\", as in iMessage.\n const barEmojis = current && !(TAPBACKS as readonly string[]).includes(current) ? [...TAPBACKS, current] : TAPBACKS;\n\n React.useEffect(() => {\n if (!visible) setPicking(false);\n }, [visible]);\n\n if (picking && message) {\n // Same Modal instance as the overlay: presenting a second modal while the first dismisses fails on iOS.\n return (\n <Modal visible transparent animationType=\"fade\" onRequestClose={onClose} statusBarTranslucent>\n <EmojiPickerSheet\n theme={theme}\n current={current}\n onPick={(emoji) => {\n setPicking(false);\n onReact(emoji);\n }}\n onClose={() => {\n setPicking(false);\n onClose();\n }}\n />\n </Modal>\n );\n }\n\n // Keep bar + bubble + menu on screen: shift the whole stack when the bubble sits near an edge.\n const bubbleH = Math.min(frame?.height ?? 0, screenH * 0.4);\n const menuH = actions.length * MENU_ROW;\n const stackH = BAR_HEIGHT + GAP + bubbleH + GAP + menuH;\n const desiredTop = (frame?.y ?? 0) - BAR_HEIGHT - GAP;\n const top = Math.max(EDGE + 44, Math.min(desiredTop, screenH - stackH - EDGE - 24));\n const align = mine ? { right: EDGE } : { left: EDGE };\n const bubbleWidth = Math.min(frame?.width ?? 0, screenW - EDGE * 2);\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"fade\"\n onRequestClose={onClose}\n onDismiss={onDismissed}\n statusBarTranslucent\n >\n <Pressable style={styles.backdrop} onPress={onClose} accessibilityLabel=\"Close message actions\">\n {message && frame ? (\n <View style={[styles.stack, { top }, align]} pointerEvents=\"box-none\">\n <View\n style={[\n styles.bar,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {barEmojis.map((emoji) => {\n const selected = emoji === current;\n return (\n <Pressable\n key={emoji}\n onPress={() => onReact(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={selected ? `Remove ${emoji} reaction` : `React ${emoji}`}\n accessibilityState={{ selected }}\n style={[styles.tapback, selected && { backgroundColor: theme.accent }]}\n testID={`chat-tapback-${emoji}`}\n >\n <Text style={styles.tapbackEmoji}>{emoji}</Text>\n </Pressable>\n );\n })}\n <Pressable\n onPress={() => setPicking(true)}\n accessibilityRole=\"button\"\n accessibilityLabel=\"More emoji\"\n style={[styles.tapback, { backgroundColor: theme.surface }]}\n testID=\"chat-tapback-more\"\n >\n <MaterialCommunityIcons name=\"plus\" size={24} color={theme.textMuted} />\n </Pressable>\n </View>\n\n <View\n style={[\n styles.preview,\n {\n width: bubbleWidth,\n maxHeight: bubbleH,\n backgroundColor: mine ? theme.bubbleMine : theme.bubbleTheirs,\n alignSelf: mine ? 'flex-end' : 'flex-start',\n },\n ]}\n >\n <Text\n numberOfLines={12}\n style={[\n styles.previewText,\n { color: mine ? theme.bubbleMineText : theme.bubbleTheirsText },\n ]}\n >\n {message.body}\n </Text>\n </View>\n\n {actions.length ? (\n <View\n style={[\n styles.menu,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {actions.map((action, i) => (\n <Pressable\n key={action.key}\n onPress={action.onPress}\n accessibilityRole=\"button\"\n style={({ pressed }) => [\n { backgroundColor: pressed ? theme.surface : 'transparent' },\n i > 0 && {\n borderTopWidth: StyleSheet.hairlineWidth,\n borderTopColor: theme.border,\n },\n ]}\n testID={`chat-message-action-${action.key}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.menuRow}>\n <Text style={[styles.menuLabel, { color: theme.text }]}>\n {action.label}\n </Text>\n <MaterialCommunityIcons name={action.icon} size={20} color={theme.text} />\n </View>\n </Pressable>\n ))}\n </View>\n ) : null}\n </View>\n ) : null}\n </Pressable>\n </Modal>\n );\n}\n\n/**\n * Any-emoji picker behind \"+\": a field for the system emoji keyboard plus a grid.\n * iOS cannot open the emoji keyboard programmatically without native code, so the\n * field opens the keyboard and the user switches to emoji with the globe key.\n */\nfunction EmojiPickerSheet({\n theme,\n current,\n onPick,\n onClose,\n}: {\n theme: ChatTheme;\n current: string | null;\n onPick: (emoji: string) => void;\n onClose: () => void;\n}) {\n const [text, setText] = React.useState('');\n const cell = Math.floor((Dimensions.get('window').width - 24) / 8);\n\n return (\n <>\n <Pressable style={styles.sheetBackdrop} onPress={onClose} accessibilityLabel=\"Close emoji picker\" />\n <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={styles.sheetDock}>\n <View style={[styles.sheet, { backgroundColor: theme.bg }]}>\n <View style={[styles.sheetHandle, { backgroundColor: theme.border }]} />\n <View style={[styles.sheetSearch, { backgroundColor: theme.surface, flexDirection: 'row' }]}>\n <MaterialCommunityIcons name=\"emoticon-outline\" size={20} color={theme.textMuted} />\n <TextInput\n value={text}\n onChangeText={(value) => {\n const emoji = emojiFromInput(value);\n if (emoji) {\n setText('');\n onPick(emoji);\n return;\n }\n setText(value);\n }}\n placeholder=\"Type any emoji (🌐 for the emoji keyboard)\"\n placeholderTextColor={theme.textMuted}\n style={[styles.sheetInput, { color: theme.text }]}\n autoCorrect={false}\n testID=\"chat-emoji-input\"\n />\n </View>\n <ScrollView style={styles.sheetScroll} keyboardShouldPersistTaps=\"handled\">\n {EMOJI_CATEGORIES.map((category) => (\n <View key={category.key}>\n <Text style={[styles.sheetTitle, { color: theme.textMuted }]}>{category.title}</Text>\n <View style={[styles.sheetGrid, { flexDirection: 'row' }]}>\n {category.emojis.map((emoji) => (\n <Pressable\n key={`${category.key}-${emoji}`}\n onPress={() => onPick(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={`React ${emoji}`}\n style={[\n {\n width: cell,\n height: cell,\n alignItems: 'center',\n justifyContent: 'center',\n },\n emoji === current && {\n backgroundColor: theme.surface,\n borderRadius: 10,\n },\n ]}\n >\n <Text style={styles.sheetEmoji}>{emoji}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n ))}\n </ScrollView>\n </View>\n </KeyboardAvoidingView>\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n sheetBackdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n sheetDock: { flex: 1, justifyContent: 'flex-end' },\n sheet: { borderTopLeftRadius: 16, borderTopRightRadius: 16, paddingTop: 8, paddingBottom: 24, maxHeight: '70%' },\n sheetHandle: { alignSelf: 'center', width: 36, height: 5, borderRadius: 3, marginBottom: 8 },\n sheetSearch: {\n alignItems: 'center',\n marginHorizontal: 12,\n marginBottom: 6,\n borderRadius: 10,\n paddingHorizontal: 10,\n height: 38,\n },\n sheetInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n sheetScroll: { paddingHorizontal: 12 },\n sheetTitle: { fontSize: 13, fontWeight: '600', marginTop: 10, marginBottom: 4 },\n sheetGrid: { flexWrap: 'wrap' },\n sheetEmoji: { fontSize: 28 },\n backdrop: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n stack: { position: 'absolute', maxWidth: '100%' },\n bar: {\n flexDirection: 'row',\n alignItems: 'center',\n height: BAR_HEIGHT,\n paddingHorizontal: 6,\n borderRadius: BAR_HEIGHT / 2,\n marginBottom: GAP,\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n tapback: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },\n tapbackEmoji: { fontSize: 24 },\n preview: { borderRadius: 18, paddingHorizontal: 14, paddingVertical: 9, overflow: 'hidden' },\n previewText: { fontSize: 17, lineHeight: 22 },\n menu: {\n marginTop: GAP,\n width: 220,\n borderRadius: 14,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n menuRow: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: MENU_ROW,\n paddingHorizontal: 16,\n },\n menuLabel: { fontSize: 16 },\n});\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MessageActions.js","sources":["../../../src/features/messaging/MessageActions.tsx"],"sourcesContent":["/**\n * iMessage-style long-press on a message: the Tapback bar above the bubble and\n * an action menu below it, over a dimmed transcript.\n *\n * Plain react-native `Modal` on purpose - the chat remote may only use what the\n * host shares, so no blur view or gesture library.\n */\nimport * as React from 'react';\nimport {\n Dimensions,\n KeyboardAvoidingView,\n Modal,\n Platform,\n Pressable,\n ScrollView,\n StyleSheet,\n Text,\n TextInput,\n View,\n} from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport type { ChatTheme } from './ChatComponents';\nimport type { ChatMessage } from './chatModel';\nimport { EMOJI_CATEGORIES, emojiFromInput, searchEmoji } from './emojiCatalog';\n\n/** The iMessage Tapback set. */\nexport const TAPBACKS = ['❤️', '👍', '👎', '😂', '‼️', '❓'] as const;\n\nexport interface BubbleFrame {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface MessageAction {\n key: string;\n label: string;\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n onPress: () => void;\n}\n\nconst BAR_HEIGHT = 52;\nconst GAP = 8;\nconst MENU_ROW = 46;\nconst EDGE = 12;\n\nexport function MessageActionsOverlay({\n message,\n frame,\n theme,\n actions,\n onReact,\n onClose,\n onDismissed,\n}: {\n message: ChatMessage | null;\n frame: BubbleFrame | null;\n theme: ChatTheme;\n actions: MessageAction[];\n onReact: (emoji: string) => void;\n onClose: () => void;\n /** Fires once the overlay is fully gone (iOS `onDismiss`), for work that needs to present UI. */\n onDismissed?: () => void;\n}) {\n const visible = Boolean(message && frame);\n const { height: screenH, width: screenW } = Dimensions.get('window');\n const mine = Boolean(message?.isMine);\n const current = message?.reactions.find((r) => r.mine)?.reaction ?? null;\n const [picking, setPicking] = React.useState(false);\n // A custom (non-Tapback) pick sits in the bar before \"+\", as in iMessage.\n const barEmojis = current && !(TAPBACKS as readonly string[]).includes(current) ? [...TAPBACKS, current] : TAPBACKS;\n\n React.useEffect(() => {\n if (!visible) setPicking(false);\n }, [visible]);\n\n if (picking && message) {\n // Same Modal instance as the overlay: presenting a second modal while the first dismisses fails on iOS.\n return (\n <Modal visible transparent animationType=\"fade\" onRequestClose={onClose} statusBarTranslucent>\n <EmojiPickerSheet\n theme={theme}\n current={current}\n onPick={(emoji) => {\n setPicking(false);\n onReact(emoji);\n }}\n onClose={() => {\n setPicking(false);\n onClose();\n }}\n />\n </Modal>\n );\n }\n\n // Keep bar + bubble + menu on screen: shift the whole stack when the bubble sits near an edge.\n const bubbleH = Math.min(frame?.height ?? 0, screenH * 0.4);\n const menuH = actions.length * MENU_ROW;\n const stackH = BAR_HEIGHT + GAP + bubbleH + GAP + menuH;\n const desiredTop = (frame?.y ?? 0) - BAR_HEIGHT - GAP;\n const top = Math.max(EDGE + 44, Math.min(desiredTop, screenH - stackH - EDGE - 24));\n const align = mine ? { right: EDGE } : { left: EDGE };\n const bubbleWidth = Math.min(frame?.width ?? 0, screenW - EDGE * 2);\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"fade\"\n onRequestClose={onClose}\n onDismiss={onDismissed}\n statusBarTranslucent\n >\n <Pressable style={styles.backdrop} onPress={onClose} accessibilityLabel=\"Close message actions\">\n {message && frame ? (\n <View style={[styles.stack, { top }, align]} pointerEvents=\"box-none\">\n <View\n style={[\n styles.bar,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {barEmojis.map((emoji) => {\n const selected = emoji === current;\n return (\n <Pressable\n key={emoji}\n onPress={() => onReact(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={selected ? `Remove ${emoji} reaction` : `React ${emoji}`}\n accessibilityState={{ selected }}\n style={[styles.tapback, selected && { backgroundColor: theme.accent }]}\n testID={`chat-tapback-${emoji}`}\n >\n <Text style={styles.tapbackEmoji}>{emoji}</Text>\n </Pressable>\n );\n })}\n <Pressable\n onPress={() => setPicking(true)}\n accessibilityRole=\"button\"\n accessibilityLabel=\"More emoji\"\n style={[styles.tapback, { backgroundColor: theme.surface }]}\n testID=\"chat-tapback-more\"\n >\n <MaterialCommunityIcons name=\"plus\" size={24} color={theme.textMuted} />\n </Pressable>\n </View>\n\n <View\n style={[\n styles.preview,\n {\n width: bubbleWidth,\n maxHeight: bubbleH,\n backgroundColor: mine ? theme.bubbleMine : theme.bubbleTheirs,\n alignSelf: mine ? 'flex-end' : 'flex-start',\n },\n ]}\n >\n <Text\n numberOfLines={12}\n style={[\n styles.previewText,\n { color: mine ? theme.bubbleMineText : theme.bubbleTheirsText },\n ]}\n >\n {message.body}\n </Text>\n </View>\n\n {actions.length ? (\n <View\n style={[\n styles.menu,\n { backgroundColor: theme.bg, alignSelf: mine ? 'flex-end' : 'flex-start' },\n ]}\n >\n {actions.map((action, i) => (\n <Pressable\n key={action.key}\n onPress={action.onPress}\n accessibilityRole=\"button\"\n style={({ pressed }) => [\n { backgroundColor: pressed ? theme.surface : 'transparent' },\n i > 0 && {\n borderTopWidth: StyleSheet.hairlineWidth,\n borderTopColor: theme.border,\n },\n ]}\n testID={`chat-message-action-${action.key}`}\n >\n {/* Inner wrapper: NativeWind can strip flexDirection from Pressable styles. */}\n <View style={styles.menuRow}>\n <Text style={[styles.menuLabel, { color: theme.text }]}>\n {action.label}\n </Text>\n <MaterialCommunityIcons name={action.icon} size={20} color={theme.text} />\n </View>\n </Pressable>\n ))}\n </View>\n ) : null}\n </View>\n ) : null}\n </Pressable>\n </Modal>\n );\n}\n\n/**\n * Any-emoji picker behind \"+\": a search field plus a grid. Typing a word searches\n * emoji by name (\"sleep\"); typing an emoji from the system keyboard (globe key)\n * reacts with it directly, so emoji missing from the grid are still reachable.\n */\nfunction EmojiPickerSheet({\n theme,\n current,\n onPick,\n onClose,\n}: {\n theme: ChatTheme;\n current: string | null;\n onPick: (emoji: string) => void;\n onClose: () => void;\n}) {\n const [text, setText] = React.useState('');\n const cell = Math.floor((Dimensions.get('window').width - 24) / 8);\n const query = text.trim();\n const results = React.useMemo(() => (query ? searchEmoji(query) : null), [query]);\n const sections = results\n ? [{ key: 'results', title: results.length ? 'Results' : 'No emoji found', emojis: results }]\n : EMOJI_CATEGORIES;\n\n return (\n <>\n <Pressable style={styles.sheetBackdrop} onPress={onClose} accessibilityLabel=\"Close emoji picker\" />\n <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={styles.sheetDock}>\n <View style={[styles.sheet, { backgroundColor: theme.bg }]}>\n <View style={[styles.sheetHandle, { backgroundColor: theme.border }]} />\n <View style={[styles.sheetSearch, { backgroundColor: theme.surface, flexDirection: 'row' }]}>\n <MaterialCommunityIcons name=\"emoticon-outline\" size={20} color={theme.textMuted} />\n <TextInput\n value={text}\n onChangeText={(value) => {\n const emoji = emojiFromInput(value);\n if (emoji) {\n setText('');\n onPick(emoji);\n return;\n }\n setText(value);\n }}\n placeholder=\"Search emoji\"\n placeholderTextColor={theme.textMuted}\n style={[styles.sheetInput, { color: theme.text }]}\n autoCorrect={false}\n autoCapitalize=\"none\"\n returnKeyType=\"search\"\n clearButtonMode=\"while-editing\"\n testID=\"chat-emoji-input\"\n />\n </View>\n <ScrollView style={styles.sheetScroll} keyboardShouldPersistTaps=\"handled\">\n {sections.map((category) => (\n <View key={category.key}>\n <Text style={[styles.sheetTitle, { color: theme.textMuted }]}>{category.title}</Text>\n <View style={[styles.sheetGrid, { flexDirection: 'row' }]}>\n {category.emojis.map((emoji) => (\n <Pressable\n key={`${category.key}-${emoji}`}\n onPress={() => onPick(emoji)}\n accessibilityRole=\"button\"\n accessibilityLabel={`React ${emoji}`}\n style={[\n {\n width: cell,\n height: cell,\n alignItems: 'center',\n justifyContent: 'center',\n },\n emoji === current && {\n backgroundColor: theme.surface,\n borderRadius: 10,\n },\n ]}\n >\n <Text style={styles.sheetEmoji}>{emoji}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n ))}\n </ScrollView>\n </View>\n </KeyboardAvoidingView>\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n sheetBackdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n sheetDock: { flex: 1, justifyContent: 'flex-end' },\n sheet: { borderTopLeftRadius: 16, borderTopRightRadius: 16, paddingTop: 8, paddingBottom: 24, maxHeight: '70%' },\n sheetHandle: { alignSelf: 'center', width: 36, height: 5, borderRadius: 3, marginBottom: 8 },\n sheetSearch: {\n alignItems: 'center',\n marginHorizontal: 12,\n marginBottom: 6,\n borderRadius: 10,\n paddingHorizontal: 10,\n height: 38,\n },\n sheetInput: { flex: 1, marginLeft: 8, fontSize: 16, paddingVertical: 0 },\n sheetScroll: { paddingHorizontal: 12 },\n sheetTitle: { fontSize: 13, fontWeight: '600', marginTop: 10, marginBottom: 4 },\n sheetGrid: { flexWrap: 'wrap' },\n sheetEmoji: { fontSize: 28 },\n backdrop: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.45)' },\n stack: { position: 'absolute', maxWidth: '100%' },\n bar: {\n flexDirection: 'row',\n alignItems: 'center',\n height: BAR_HEIGHT,\n paddingHorizontal: 6,\n borderRadius: BAR_HEIGHT / 2,\n marginBottom: GAP,\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n tapback: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },\n tapbackEmoji: { fontSize: 24 },\n preview: { borderRadius: 18, paddingHorizontal: 14, paddingVertical: 9, overflow: 'hidden' },\n previewText: { fontSize: 17, lineHeight: 22 },\n menu: {\n marginTop: GAP,\n width: 220,\n borderRadius: 14,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOpacity: 0.18,\n shadowRadius: 12,\n shadowOffset: { width: 0, height: 4 },\n elevation: 6,\n },\n menuRow: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: MENU_ROW,\n paddingHorizontal: 16,\n },\n menuLabel: { fontSize: 16 },\n});\n"],"names":[],"mappings":"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"}
@@ -154,7 +154,10 @@ function NativeChatExperience({
154
154
  } : void 0, onDismissError: () => setDismissedError(thread.error) }) });
155
155
  }
156
156
  if (route === "contacts") {
157
- return /* @__PURE__ */ jsx(Shell, { theme, testID, children: /* @__PURE__ */ jsx(ContactsScreen, { permission: contactMatches.permission, loading: contactMatches.loading, error: startChatError != null ? startChatError : contactMatches.error, onYantra: contactMatches.onYantra, invitable: contactMatches.invitable, theme, busyContactId, onBack: () => setRoute("list"), onRequestAccess: () => void contactMatches.requestAccess(), onOpenSettings: () => void contactMatches.openSettings(), onStartChat: (c) => void startChatWith(c), onInvite: (c) => void inviteContact(c), onDismissError: () => {
157
+ return /* @__PURE__ */ jsx(Shell, { theme, testID, children: /* @__PURE__ */ jsx(ContactsScreen, { permission: contactMatches.permission, loading: contactMatches.loading, error: startChatError != null ? startChatError : contactMatches.error, onYantra: contactMatches.onYantra, invitable: contactMatches.invitable, theme, busyContactId, onBack: () => setRoute("list"), onRequestAccess: () => void contactMatches.requestAccess(), onOpenSettings: () => void contactMatches.openSettings(), onStartChat: (c) => void startChatWith(c), onInvite: (c) => void inviteContact(c), limited: contactMatches.limited, onChooseMoreContacts: contactMatches.chooseMoreContacts ? () => {
158
+ var _a;
159
+ return void ((_a = contactMatches.chooseMoreContacts) == null ? void 0 : _a.call(contactMatches));
160
+ } : void 0, onDismissError: () => {
158
161
  if (startChatError) setStartChatError(null);
159
162
  else void contactMatches.refresh();
160
163
  } }) });