@adminide-stack/yantra-mobile 12.0.70-alpha.4 → 12.0.70
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.
- package/lib/config/env-config.js +1 -1
- package/lib/config/env-config.js.map +1 -1
- package/lib/features/messaging/ChatAttachments.js +383 -0
- package/lib/features/messaging/ChatAttachments.js.map +1 -0
- package/lib/features/messaging/ChatComponents.js +22 -6
- package/lib/features/messaging/ChatComponents.js.map +1 -1
- package/lib/features/messaging/ContactsScreen.js +15 -16
- package/lib/features/messaging/ContactsScreen.js.map +1 -1
- package/lib/features/messaging/NativeChatExperience.js +5 -1
- package/lib/features/messaging/NativeChatExperience.js.map +1 -1
- package/lib/features/messaging/ThreadScreen.js +88 -13
- package/lib/features/messaging/ThreadScreen.js.map +1 -1
- package/lib/features/messaging/chatModel.js +93 -7
- package/lib/features/messaging/chatModel.js.map +1 -1
- package/lib/features/messaging/chatOperations.js +27 -6
- package/lib/features/messaging/chatOperations.js.map +1 -1
- package/lib/features/messaging/useChatData.js +125 -6
- package/lib/features/messaging/useChatData.js.map +1 -1
- package/package.json +4 -4
package/lib/config/env-config.js
CHANGED
|
@@ -103,7 +103,7 @@ const baseConfig = cleanEnv(env, {
|
|
|
103
103
|
}),
|
|
104
104
|
/** Yantra Chat registration: offer "Continue with test OTP" (123456, no SMS). Never on App Store builds. */
|
|
105
105
|
CHAT_ALLOW_TEST_OTP: bool({
|
|
106
|
-
default:
|
|
106
|
+
default: false
|
|
107
107
|
}),
|
|
108
108
|
PROXY_STAGING_GATEWAY_URL: str({
|
|
109
109
|
default: ""
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-config.js","sources":["../../src/config/env-config.ts"],"sourcesContent":["import { str, cleanEnv, bool, num } from 'envalid';\n\nconst env = (process as { APP_ENV?: NodeJS.ProcessEnv; env?: NodeJS.ProcessEnv }).APP_ENV || process.env;\n\nconst baseConfig = cleanEnv(env, {\n GRAPHQL_URL: str({ default: 'http://localhost:8080/graphql' }),\n CLIENT_URL: str({ default: 'http://localhost:3000' }),\n /** Standalone Web Builder surface embedded in the mobile WebView shell. */\n WEB_BUILDER_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Design surface — local vite is 5174/4174 next to canvas 5175. */\n DESIGN_SURFACE_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Canvas surface embedded in the same WebView shell. */\n CANVAS_SURFACE_URL: str({ default: 'http://localhost:5175/' }),\n /**\n * GraphQL backend whose JWT keys the canvas index will verify.\n * Must stay in lockstep with canvas-surface `.env.development`\n * (`VITE_BACKEND_GRAPHQL_URL` / `VITE_SURFACE_INDEX_URL`). A locally\n * minted token 401s against `surface-index-backend.yantra-app-v1`.\n */\n CANVAS_BACKEND_GRAPHQL_URL: str({ default: 'https://ideback.yantra-app-v1.cdebase.dev/graphql' }),\n /** Explicit canvas index GraphQL URL. Empty → derived from the canvas auth backend. */\n SURFACE_INDEX_URL: str({ default: '' }),\n /**\n * Origin template for every other deployed surface: `{slug}` is the app's\n * `surfaceSlug`. One var instead of 20+ near-identical URLs, and it keeps the\n * environment (dev cluster / prod) in one place.\n */\n SURFACE_URL_TEMPLATE: str({ default: 'https://{slug}-surface.yantra-app-v1.cdebase.dev/' }),\n /**\n * Characters gallery manifest (`characters.json`). Empty by default →\n * DERIVED from SURFACE_URL_TEMPLATE + the wipe-immune `characters-native`\n * slug (see below), so a prod build reads prod and a dev build reads dev\n * with no per-profile URL to maintain. Set explicitly only to pin a\n * non-standard origin.\n */\n CHARACTERS_NATIVE_MANIFEST_URL: str({ default: '' }),\n /**\n * Character community subgraph GraphQL URL the federated character studio\n * reads (Discover feed, publish/edit). Empty by default → DERIVED from the\n * same backend the surface token is minted against (swap the first DNS label\n * `ideback.x.y` → `character-subgraph.x.y`, the convention the character\n * surfaces use). Set explicitly only to pin a non-standard origin.\n */\n CHARACTERS_SUBGRAPH_URL: str({ default: '' }),\n APP_NAME: str({ default: 'Yantra' }),\n GROQ_API_KEY: str({ default: '' }),\n CDECLI_AGENT_ENDPOINT: str({ default: 'https://cdecli-agent.cdebase.dev' }),\n CDECLI_AGENT_AUTH_TOKEN: str({ default: '' }),\n /** Seconds to wait for CDeCLI stream completion before timing out (then fallback chat API runs). Min 15, max 600. */\n /** Aligned with web `useCdecliChannel` (120s). Mobile hook uses the same constant inline. */\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: num({ default: 120 }),\n BACKEND_URL: str({ default: '' }),\n USE_PROXY_STAGING_GATEWAY: bool({ default: false }),\n /** Yantra Chat registration: offer \"Continue with test OTP\" (123456, no SMS). Never on App Store builds. */\n CHAT_ALLOW_TEST_OTP: bool({ default: true }),\n PROXY_STAGING_GATEWAY_URL: str({ default: '' }),\n});\n\n/**\n * Where the app fetches the characters gallery manifest.\n *\n * Mirrors metro.federation.js `charactersRemoteUrl()`: the manifest lives on\n * the dedicated `characters-native` artifacts slug, NOT `character-surface`.\n * The bulk web-surface republish redeploys character-surface from its pinned\n * web bundle and wipes anything hand-added under /native, so the gallery data\n * (like the federated remote) must ride the wipe-immune slug. The origin\n * DERIVES from SURFACE_URL_TEMPLATE, so prod builds read prod and dev builds\n * read dev automatically. An explicit CHARACTERS_NATIVE_MANIFEST_URL still\n * wins for a one-off pin.\n */\nfunction resolveCharactersManifestUrl(explicit: string, template: string): string {\n if (explicit) return explicit;\n const base = (template || 'https://{slug}-surface.yantra-app-v1.cdebase.dev/')\n .replace('{slug}', 'characters-native')\n .replace(/\\/+$/, '');\n return `${base}/native/characters.json`;\n}\n\n/**\n * Character community subgraph URL. Derives from the SAME backend the surface\n * session token is minted against, so it stays authoritative across dev/prod and\n * matches the token's audience: a locally minted token is scoped to\n * CANVAS_BACKEND_GRAPHQL_URL (the cluster backend), so a local GRAPHQL_URL uses\n * that cluster host too - a subgraph on localhost would 401 the same JWT.\n */\nfunction resolveCharacterSubgraphUrl(explicit: string, graphqlUrl: string, canvasBackend: string): string {\n if (explicit) return explicit;\n const isLocal = /^https?:\\/\\/(localhost|127\\.0\\.0\\.1|\\[::1\\])(:|\\/|$)/i.test((graphqlUrl || '').trim());\n const backend = isLocal ? canvasBackend : graphqlUrl;\n const match = (backend || '').trim().match(/^(https?:)\\/\\/([^/]+)/i);\n if (!match) return '';\n const labels = match[2].split('.');\n if (labels.length < 3) return '';\n return `${match[1]}//character-subgraph.${labels.slice(1).join('.')}/graphql`;\n}\n\nfunction resolveAgentGatewayGraphqlUrl(): string {\n if (baseConfig.USE_PROXY_STAGING_GATEWAY && baseConfig.PROXY_STAGING_GATEWAY_URL) {\n return baseConfig.PROXY_STAGING_GATEWAY_URL;\n }\n if (baseConfig.GRAPHQL_URL) return baseConfig.GRAPHQL_URL;\n if (baseConfig.BACKEND_URL) return `${baseConfig.BACKEND_URL.replace(/\\/$/, '')}/graphql`;\n return '/graphql';\n}\n\nfunction resolveBaseWsUrl(graphqlUrl: string): string {\n const base = graphqlUrl.replace(/\\/graphql$/, '');\n return base.replace(/^http/, 'ws');\n}\n\nfunction resolveAgentGatewayWsUrl(graphqlUrl: string): string {\n return `${resolveBaseWsUrl(graphqlUrl)}/ws/openclaw-chat`;\n}\n\nconst agentGatewayGraphqlUrl = resolveAgentGatewayGraphqlUrl();\n\nconst clampedCdecliTimeoutSec = Math.min(600, Math.max(15, Math.round(baseConfig.CDECLI_CHAT_RESPONSE_TIMEOUT_SEC)));\n\nexport const config = {\n ...baseConfig,\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: clampedCdecliTimeoutSec,\n CHARACTERS_NATIVE_MANIFEST_URL: resolveCharactersManifestUrl(\n baseConfig.CHARACTERS_NATIVE_MANIFEST_URL,\n baseConfig.SURFACE_URL_TEMPLATE,\n ),\n CHARACTERS_SUBGRAPH_URL: resolveCharacterSubgraphUrl(\n baseConfig.CHARACTERS_SUBGRAPH_URL,\n baseConfig.GRAPHQL_URL,\n baseConfig.CANVAS_BACKEND_GRAPHQL_URL,\n ),\n AGENT_GATEWAY_GRAPHQL_URL: agentGatewayGraphqlUrl,\n AGENT_GATEWAY_WS_URL: resolveAgentGatewayWsUrl(agentGatewayGraphqlUrl),\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AACA,MAAM,GAAA,GAAO,OAGV,CAAA,OAAA,IAAW,OAAQ,CAAA,GAAA;AACtB,MAAM,UAAA,GAAa,SAAS,GAAK,EAAA;AAAA,EAC/B,aAAa,GAAI,CAAA;AAAA,IACf,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,YAAY,GAAI,CAAA;AAAA,IACd,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,iBAAiB,GAAI,CAAA;AAAA,IACnB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,oBAAoB,GAAI,CAAA;AAAA,IACtB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,oBAAoB,GAAI,CAAA;AAAA,IACtB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOD,4BAA4B,GAAI,CAAA;AAAA,IAC9B,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,mBAAmB,GAAI,CAAA;AAAA,IACrB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMD,sBAAsB,GAAI,CAAA;AAAA,IACxB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQD,gCAAgC,GAAI,CAAA;AAAA,IAClC,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQD,yBAAyB,GAAI,CAAA;AAAA,IAC3B,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,UAAU,GAAI,CAAA;AAAA,IACZ,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,cAAc,GAAI,CAAA;AAAA,IAChB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,uBAAuB,GAAI,CAAA;AAAA,IACzB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,yBAAyB,GAAI,CAAA;AAAA,IAC3B,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA,EAGD,kCAAkC,GAAI,CAAA;AAAA,IACpC,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,aAAa,GAAI,CAAA;AAAA,IACf,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,2BAA2B,IAAK,CAAA;AAAA,IAC9B,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,qBAAqB,IAAK,CAAA;AAAA,IACxB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,2BAA2B,GAAI,CAAA;AAAA,IAC7B,OAAS,EAAA;AAAA,GACV;AACH,CAAC,CAAA;AAcD,SAAS,4BAAA,CAA6B,UAAkB,QAA0B,EAAA;AAChF,EAAA,IAAI,UAAiB,OAAA,QAAA;AACrB,EAAM,MAAA,IAAA,GAAA,CAAQ,YAAY,mDAAqD,EAAA,OAAA,CAAQ,UAAU,mBAAmB,CAAA,CAAE,OAAQ,CAAA,MAAA,EAAQ,EAAE,CAAA;AACxI,EAAA,OAAO,GAAG,IAAI,CAAA,uBAAA,CAAA;AAChB;AASA,SAAS,2BAAA,CAA4B,QAAkB,EAAA,UAAA,EAAoB,aAA+B,EAAA;AACxG,EAAA,IAAI,UAAiB,OAAA,QAAA;AACrB,EAAA,MAAM,UAAU,uDAAwD,CAAA,IAAA,CAAA,CAAM,UAAc,IAAA,EAAA,EAAI,MAAM,CAAA;AACtG,EAAM,MAAA,OAAA,GAAU,UAAU,aAAgB,GAAA,UAAA;AAC1C,EAAA,MAAM,SAAS,OAAW,IAAA,EAAA,EAAI,IAAK,EAAA,CAAE,MAAM,wBAAwB,CAAA;AACnE,EAAI,IAAA,CAAC,OAAc,OAAA,EAAA;AACnB,EAAA,MAAM,MAAS,GAAA,KAAA,CAAM,CAAC,CAAA,CAAE,MAAM,GAAG,CAAA;AACjC,EAAI,IAAA,MAAA,CAAO,MAAS,GAAA,CAAA,EAAU,OAAA,EAAA;AAC9B,EAAO,OAAA,CAAA,EAAG,KAAM,CAAA,CAAC,CAAC,CAAA,qBAAA,EAAwB,MAAO,CAAA,KAAA,CAAM,CAAC,CAAA,CAAE,IAAK,CAAA,GAAG,CAAC,CAAA,QAAA,CAAA;AACrE;AACA,SAAS,6BAAwC,GAAA;AAC/C,EAAI,IAAA,UAAA,CAAW,yBAA6B,IAAA,UAAA,CAAW,yBAA2B,EAAA;AAChF,IAAA,OAAO,UAAW,CAAA,yBAAA;AAAA;AAEpB,EAAI,IAAA,UAAA,CAAW,WAAa,EAAA,OAAO,UAAW,CAAA,WAAA;AAC9C,EAAI,IAAA,UAAA,CAAW,aAAoB,OAAA,CAAA,EAAG,WAAW,WAAY,CAAA,OAAA,CAAQ,KAAO,EAAA,EAAE,CAAC,CAAA,QAAA,CAAA;AAC/E,EAAO,OAAA,UAAA;AACT;AACA,SAAS,iBAAiB,UAA4B,EAAA;AACpD,EAAA,MAAM,IAAO,GAAA,UAAA,CAAW,OAAQ,CAAA,YAAA,EAAc,EAAE,CAAA;AAChD,EAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,OAAA,EAAS,IAAI,CAAA;AACnC;AACA,SAAS,yBAAyB,UAA4B,EAAA;AAC5D,EAAO,OAAA,CAAA,EAAG,gBAAiB,CAAA,UAAU,CAAC,CAAA,iBAAA,CAAA;AACxC;AACA,MAAM,yBAAyB,6BAA8B,EAAA;AAC7D,MAAM,uBAA0B,GAAA,IAAA,CAAK,GAAI,CAAA,GAAA,EAAK,IAAK,CAAA,GAAA,CAAI,EAAI,EAAA,IAAA,CAAK,KAAM,CAAA,UAAA,CAAW,gCAAgC,CAAC,CAAC,CAAA;AACtG,MAAA,MAAA,GAAS,iCACjB,UADiB,CAAA,EAAA;AAAA,EAEpB,gCAAkC,EAAA,uBAAA;AAAA,EAClC,8BAAgC,EAAA,4BAAA,CAA6B,UAAW,CAAA,8BAAA,EAAgC,WAAW,oBAAoB,CAAA;AAAA,EACvI,yBAAyB,2BAA4B,CAAA,UAAA,CAAW,yBAAyB,UAAW,CAAA,WAAA,EAAa,WAAW,0BAA0B,CAAA;AAAA,EACtJ,yBAA2B,EAAA,sBAAA;AAAA,EAC3B,oBAAA,EAAsB,yBAAyB,sBAAsB;AACvE,CAAA"}
|
|
1
|
+
{"version":3,"file":"env-config.js","sources":["../../src/config/env-config.ts"],"sourcesContent":["import { str, cleanEnv, bool, num } from 'envalid';\n\nconst env = (process as { APP_ENV?: NodeJS.ProcessEnv; env?: NodeJS.ProcessEnv }).APP_ENV || process.env;\n\nconst baseConfig = cleanEnv(env, {\n GRAPHQL_URL: str({ default: 'http://localhost:8080/graphql' }),\n CLIENT_URL: str({ default: 'http://localhost:3000' }),\n /** Standalone Web Builder surface embedded in the mobile WebView shell. */\n WEB_BUILDER_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Design surface — local vite is 5174/4174 next to canvas 5175. */\n DESIGN_SURFACE_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Canvas surface embedded in the same WebView shell. */\n CANVAS_SURFACE_URL: str({ default: 'http://localhost:5175/' }),\n /**\n * GraphQL backend whose JWT keys the canvas index will verify.\n * Must stay in lockstep with canvas-surface `.env.development`\n * (`VITE_BACKEND_GRAPHQL_URL` / `VITE_SURFACE_INDEX_URL`). A locally\n * minted token 401s against `surface-index-backend.yantra-app-v1`.\n */\n CANVAS_BACKEND_GRAPHQL_URL: str({ default: 'https://ideback.yantra-app-v1.cdebase.dev/graphql' }),\n /** Explicit canvas index GraphQL URL. Empty → derived from the canvas auth backend. */\n SURFACE_INDEX_URL: str({ default: '' }),\n /**\n * Origin template for every other deployed surface: `{slug}` is the app's\n * `surfaceSlug`. One var instead of 20+ near-identical URLs, and it keeps the\n * environment (dev cluster / prod) in one place.\n */\n SURFACE_URL_TEMPLATE: str({ default: 'https://{slug}-surface.yantra-app-v1.cdebase.dev/' }),\n /**\n * Characters gallery manifest (`characters.json`). Empty by default →\n * DERIVED from SURFACE_URL_TEMPLATE + the wipe-immune `characters-native`\n * slug (see below), so a prod build reads prod and a dev build reads dev\n * with no per-profile URL to maintain. Set explicitly only to pin a\n * non-standard origin.\n */\n CHARACTERS_NATIVE_MANIFEST_URL: str({ default: '' }),\n /**\n * Character community subgraph GraphQL URL the federated character studio\n * reads (Discover feed, publish/edit). Empty by default → DERIVED from the\n * same backend the surface token is minted against (swap the first DNS label\n * `ideback.x.y` → `character-subgraph.x.y`, the convention the character\n * surfaces use). Set explicitly only to pin a non-standard origin.\n */\n CHARACTERS_SUBGRAPH_URL: str({ default: '' }),\n APP_NAME: str({ default: 'Yantra' }),\n GROQ_API_KEY: str({ default: '' }),\n CDECLI_AGENT_ENDPOINT: str({ default: 'https://cdecli-agent.cdebase.dev' }),\n CDECLI_AGENT_AUTH_TOKEN: str({ default: '' }),\n /** Seconds to wait for CDeCLI stream completion before timing out (then fallback chat API runs). Min 15, max 600. */\n /** Aligned with web `useCdecliChannel` (120s). Mobile hook uses the same constant inline. */\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: num({ default: 120 }),\n BACKEND_URL: str({ default: '' }),\n USE_PROXY_STAGING_GATEWAY: bool({ default: false }),\n /** Yantra Chat registration: offer \"Continue with test OTP\" (123456, no SMS). Never on App Store builds. */\n CHAT_ALLOW_TEST_OTP: bool({ default: false }),\n PROXY_STAGING_GATEWAY_URL: str({ default: '' }),\n});\n\n/**\n * Where the app fetches the characters gallery manifest.\n *\n * Mirrors metro.federation.js `charactersRemoteUrl()`: the manifest lives on\n * the dedicated `characters-native` artifacts slug, NOT `character-surface`.\n * The bulk web-surface republish redeploys character-surface from its pinned\n * web bundle and wipes anything hand-added under /native, so the gallery data\n * (like the federated remote) must ride the wipe-immune slug. The origin\n * DERIVES from SURFACE_URL_TEMPLATE, so prod builds read prod and dev builds\n * read dev automatically. An explicit CHARACTERS_NATIVE_MANIFEST_URL still\n * wins for a one-off pin.\n */\nfunction resolveCharactersManifestUrl(explicit: string, template: string): string {\n if (explicit) return explicit;\n const base = (template || 'https://{slug}-surface.yantra-app-v1.cdebase.dev/')\n .replace('{slug}', 'characters-native')\n .replace(/\\/+$/, '');\n return `${base}/native/characters.json`;\n}\n\n/**\n * Character community subgraph URL. Derives from the SAME backend the surface\n * session token is minted against, so it stays authoritative across dev/prod and\n * matches the token's audience: a locally minted token is scoped to\n * CANVAS_BACKEND_GRAPHQL_URL (the cluster backend), so a local GRAPHQL_URL uses\n * that cluster host too - a subgraph on localhost would 401 the same JWT.\n */\nfunction resolveCharacterSubgraphUrl(explicit: string, graphqlUrl: string, canvasBackend: string): string {\n if (explicit) return explicit;\n const isLocal = /^https?:\\/\\/(localhost|127\\.0\\.0\\.1|\\[::1\\])(:|\\/|$)/i.test((graphqlUrl || '').trim());\n const backend = isLocal ? canvasBackend : graphqlUrl;\n const match = (backend || '').trim().match(/^(https?:)\\/\\/([^/]+)/i);\n if (!match) return '';\n const labels = match[2].split('.');\n if (labels.length < 3) return '';\n return `${match[1]}//character-subgraph.${labels.slice(1).join('.')}/graphql`;\n}\n\nfunction resolveAgentGatewayGraphqlUrl(): string {\n if (baseConfig.USE_PROXY_STAGING_GATEWAY && baseConfig.PROXY_STAGING_GATEWAY_URL) {\n return baseConfig.PROXY_STAGING_GATEWAY_URL;\n }\n if (baseConfig.GRAPHQL_URL) return baseConfig.GRAPHQL_URL;\n if (baseConfig.BACKEND_URL) return `${baseConfig.BACKEND_URL.replace(/\\/$/, '')}/graphql`;\n return '/graphql';\n}\n\nfunction resolveBaseWsUrl(graphqlUrl: string): string {\n const base = graphqlUrl.replace(/\\/graphql$/, '');\n return base.replace(/^http/, 'ws');\n}\n\nfunction resolveAgentGatewayWsUrl(graphqlUrl: string): string {\n return `${resolveBaseWsUrl(graphqlUrl)}/ws/openclaw-chat`;\n}\n\nconst agentGatewayGraphqlUrl = resolveAgentGatewayGraphqlUrl();\n\nconst clampedCdecliTimeoutSec = Math.min(600, Math.max(15, Math.round(baseConfig.CDECLI_CHAT_RESPONSE_TIMEOUT_SEC)));\n\nexport const config = {\n ...baseConfig,\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: clampedCdecliTimeoutSec,\n CHARACTERS_NATIVE_MANIFEST_URL: resolveCharactersManifestUrl(\n baseConfig.CHARACTERS_NATIVE_MANIFEST_URL,\n baseConfig.SURFACE_URL_TEMPLATE,\n ),\n CHARACTERS_SUBGRAPH_URL: resolveCharacterSubgraphUrl(\n baseConfig.CHARACTERS_SUBGRAPH_URL,\n baseConfig.GRAPHQL_URL,\n baseConfig.CANVAS_BACKEND_GRAPHQL_URL,\n ),\n AGENT_GATEWAY_GRAPHQL_URL: agentGatewayGraphqlUrl,\n AGENT_GATEWAY_WS_URL: resolveAgentGatewayWsUrl(agentGatewayGraphqlUrl),\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,ScrollView,View,Pressable,Text,Modal,ActivityIndicator}from'react-native';import {Image}from'expo-image';import {MaterialCommunityIcons}from'@expo/vector-icons';import {SafeAreaView,SafeAreaProvider}from'react-native-safe-area-context';import {attachmentUrl}from'./chatModel.js';const IMAGE_MAX = 240;
|
|
2
|
+
function durationLabel(ms) {
|
|
3
|
+
const total = Math.max(0, Math.round((ms != null ? ms : 0) / 1e3));
|
|
4
|
+
return `${Math.floor(total / 60)}:${String(total % 60).padStart(2, "0")}`;
|
|
5
|
+
}
|
|
6
|
+
function imageSize(a) {
|
|
7
|
+
var _a, _b;
|
|
8
|
+
const w = (_a = a.width) != null ? _a : 0;
|
|
9
|
+
const h = (_b = a.height) != null ? _b : 0;
|
|
10
|
+
if (!w || !h) return {
|
|
11
|
+
width: IMAGE_MAX,
|
|
12
|
+
height: IMAGE_MAX * 0.75
|
|
13
|
+
};
|
|
14
|
+
const scale = Math.min(IMAGE_MAX / w, IMAGE_MAX / h);
|
|
15
|
+
return {
|
|
16
|
+
width: Math.max(120, Math.round(w * scale)),
|
|
17
|
+
height: Math.max(90, Math.round(h * scale))
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function MessageAttachments({
|
|
21
|
+
attachments,
|
|
22
|
+
mine,
|
|
23
|
+
theme,
|
|
24
|
+
fileBaseUrl,
|
|
25
|
+
audio,
|
|
26
|
+
uploading,
|
|
27
|
+
onOpenImage
|
|
28
|
+
}) {
|
|
29
|
+
const images = attachments.filter((a) => a.kind === "image");
|
|
30
|
+
const voices = attachments.filter((a) => a.kind === "audio");
|
|
31
|
+
const others = attachments.filter((a) => a.kind === "file");
|
|
32
|
+
const textColor = mine ? theme.bubbleMineText : theme.bubbleTheirsText;
|
|
33
|
+
return /* @__PURE__ */ jsxs(View, { children: [
|
|
34
|
+
images.length ? /* @__PURE__ */ jsx(View, { style: [styles.imageGrid, {
|
|
35
|
+
flexDirection: "row"
|
|
36
|
+
}], children: images.map((a) => {
|
|
37
|
+
const url = attachmentUrl(a, fileBaseUrl);
|
|
38
|
+
const size = images.length === 1 ? imageSize(a) : {
|
|
39
|
+
width: 116,
|
|
40
|
+
height: 116
|
|
41
|
+
};
|
|
42
|
+
return /* @__PURE__ */ jsxs(Pressable, { onPress: onOpenImage ? () => onOpenImage(url) : void 0, accessibilityRole: "imagebutton", accessibilityLabel: "Photo", children: [
|
|
43
|
+
/* @__PURE__ */ jsx(Image, { source: {
|
|
44
|
+
uri: url
|
|
45
|
+
}, style: [styles.image, size, {
|
|
46
|
+
backgroundColor: theme.surface
|
|
47
|
+
}], contentFit: "cover", transition: 120 }),
|
|
48
|
+
uploading ? /* @__PURE__ */ jsx(View, { style: [StyleSheet.absoluteFill, styles.imageOverlay], children: /* @__PURE__ */ jsx(ActivityIndicator, { color: "#ffffff" }) }) : null
|
|
49
|
+
] }, a.id);
|
|
50
|
+
}) }) : null,
|
|
51
|
+
voices.map((a) => /* @__PURE__ */ jsx(VoiceNote, { url: attachmentUrl(a, fileBaseUrl), durationMs: a.durationMs, audio, color: textColor, accent: mine ? textColor : theme.accent, disabled: uploading }, a.id)),
|
|
52
|
+
others.map((a) => /* @__PURE__ */ jsxs(View, { style: [styles.fileRow, {
|
|
53
|
+
flexDirection: "row"
|
|
54
|
+
}], children: [
|
|
55
|
+
/* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "file-outline", size: 20, color: textColor }),
|
|
56
|
+
/* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: [styles.fileName, {
|
|
57
|
+
color: textColor
|
|
58
|
+
}], children: a.name })
|
|
59
|
+
] }, a.id))
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
function VoiceNote({
|
|
63
|
+
url,
|
|
64
|
+
durationMs,
|
|
65
|
+
audio,
|
|
66
|
+
color,
|
|
67
|
+
accent,
|
|
68
|
+
disabled
|
|
69
|
+
}) {
|
|
70
|
+
const [playing, setPlaying] = React.useState(false);
|
|
71
|
+
const stopRef = React.useRef(null);
|
|
72
|
+
React.useEffect(() => () => {
|
|
73
|
+
var _a;
|
|
74
|
+
return (_a = stopRef.current) == null ? void 0 : _a.call(stopRef);
|
|
75
|
+
}, []);
|
|
76
|
+
const toggle = React.useCallback(async () => {
|
|
77
|
+
var _a;
|
|
78
|
+
if (!audio) return;
|
|
79
|
+
if (playing) {
|
|
80
|
+
(_a = stopRef.current) == null ? void 0 : _a.call(stopRef);
|
|
81
|
+
stopRef.current = null;
|
|
82
|
+
setPlaying(false);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
setPlaying(true);
|
|
86
|
+
try {
|
|
87
|
+
stopRef.current = await audio.play(url, () => {
|
|
88
|
+
stopRef.current = null;
|
|
89
|
+
setPlaying(false);
|
|
90
|
+
});
|
|
91
|
+
} catch (e) {
|
|
92
|
+
setPlaying(false);
|
|
93
|
+
}
|
|
94
|
+
}, [audio, playing, url]);
|
|
95
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.voice, {
|
|
96
|
+
flexDirection: "row"
|
|
97
|
+
}], children: [
|
|
98
|
+
/* @__PURE__ */ jsx(Pressable, { onPress: () => void toggle(), disabled: !audio || disabled, hitSlop: 8, accessibilityRole: "button", accessibilityLabel: playing ? "Pause voice message" : "Play voice message", children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: playing ? "pause-circle" : "play-circle", size: 34, color: accent }) }),
|
|
99
|
+
/* @__PURE__ */ jsx(View, { style: [styles.voiceWave, {
|
|
100
|
+
flexDirection: "row"
|
|
101
|
+
}], children: WAVE.map((h, i) => /* @__PURE__ */ jsx(View, { style: [styles.voiceBar, {
|
|
102
|
+
height: h,
|
|
103
|
+
backgroundColor: color,
|
|
104
|
+
opacity: 0.55
|
|
105
|
+
}] }, i)) }),
|
|
106
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.voiceTime, {
|
|
107
|
+
color
|
|
108
|
+
}], children: durationLabel(durationMs) })
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
const WAVE = [6, 12, 18, 10, 22, 14, 8, 16, 24, 12, 6, 14, 20, 10, 16, 8, 12, 18];
|
|
112
|
+
function ImageViewer({
|
|
113
|
+
url,
|
|
114
|
+
onClose
|
|
115
|
+
}) {
|
|
116
|
+
return /* @__PURE__ */ jsx(Modal, { visible: Boolean(url), transparent: true, animationType: "fade", statusBarTranslucent: true, onRequestClose: onClose, children: /* @__PURE__ */ jsx(SafeAreaProvider, { children: /* @__PURE__ */ jsxs(View, { style: styles.viewer, children: [
|
|
117
|
+
url ? /* @__PURE__ */ jsx(Pressable, { style: StyleSheet.absoluteFill, onPress: onClose, accessibilityLabel: "Close photo", children: /* @__PURE__ */ jsx(Image, { source: {
|
|
118
|
+
uri: url
|
|
119
|
+
}, style: StyleSheet.absoluteFill, contentFit: "contain" }) }) : null,
|
|
120
|
+
/* @__PURE__ */ jsx(SafeAreaView, { edges: ["top"], style: styles.viewerTop, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: onClose, hitSlop: 16, accessibilityRole: "button", accessibilityLabel: "Close", style: styles.viewerClose, testID: "chat-image-viewer-close", children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "close", size: 24, color: "#ffffff" }) }) })
|
|
121
|
+
] }) }) });
|
|
122
|
+
}
|
|
123
|
+
function AttachSheet({
|
|
124
|
+
visible,
|
|
125
|
+
theme,
|
|
126
|
+
onClose,
|
|
127
|
+
onPhotos,
|
|
128
|
+
onCamera,
|
|
129
|
+
onDismissed
|
|
130
|
+
}) {
|
|
131
|
+
const row = (icon, label, run) => /* @__PURE__ */ jsx(Pressable, { onPress: run, accessibilityRole: "button", accessibilityLabel: label, style: ({
|
|
132
|
+
pressed
|
|
133
|
+
}) => ({
|
|
134
|
+
backgroundColor: pressed ? theme.surface : "transparent"
|
|
135
|
+
}), children: /* @__PURE__ */ jsxs(View, { style: [styles.sheetRow, {
|
|
136
|
+
flexDirection: "row"
|
|
137
|
+
}], children: [
|
|
138
|
+
/* @__PURE__ */ jsx(View, { style: [styles.sheetIcon, {
|
|
139
|
+
backgroundColor: theme.surface
|
|
140
|
+
}], children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: icon, size: 22, color: theme.accent }) }),
|
|
141
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.sheetLabel, {
|
|
142
|
+
color: theme.text
|
|
143
|
+
}], children: label })
|
|
144
|
+
] }) });
|
|
145
|
+
return /* @__PURE__ */ jsxs(Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onClose, onDismiss: onDismissed, children: [
|
|
146
|
+
/* @__PURE__ */ jsx(Pressable, { style: styles.sheetBackdrop, onPress: onClose, accessibilityLabel: "Close" }),
|
|
147
|
+
/* @__PURE__ */ jsxs(SafeAreaView, { edges: ["bottom"], style: [styles.sheet, {
|
|
148
|
+
backgroundColor: theme.bg
|
|
149
|
+
}], children: [
|
|
150
|
+
/* @__PURE__ */ jsx(View, { style: [styles.sheetHandle, {
|
|
151
|
+
backgroundColor: theme.border
|
|
152
|
+
}] }),
|
|
153
|
+
row("image-multiple-outline", "Photos", onPhotos),
|
|
154
|
+
row("camera-outline", "Camera", onCamera)
|
|
155
|
+
] })
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
function StagedAttachments({
|
|
159
|
+
files,
|
|
160
|
+
theme,
|
|
161
|
+
onRemove
|
|
162
|
+
}) {
|
|
163
|
+
if (!files.length) return null;
|
|
164
|
+
return /* @__PURE__ */ jsx(
|
|
165
|
+
ScrollView,
|
|
166
|
+
{
|
|
167
|
+
horizontal: true,
|
|
168
|
+
showsHorizontalScrollIndicator: false,
|
|
169
|
+
style: [styles.stagedBar, {
|
|
170
|
+
backgroundColor: theme.composerBar
|
|
171
|
+
}],
|
|
172
|
+
contentContainerStyle: styles.staged,
|
|
173
|
+
children: files.map((f, i) => /* @__PURE__ */ jsxs(View, { style: styles.stagedItem, children: [
|
|
174
|
+
/* @__PURE__ */ jsx(Image, { source: {
|
|
175
|
+
uri: f.uri
|
|
176
|
+
}, style: styles.stagedImage, contentFit: "cover" }),
|
|
177
|
+
/* @__PURE__ */ jsx(Pressable, { onPress: () => onRemove(i), hitSlop: 8, accessibilityRole: "button", accessibilityLabel: "Remove photo", style: styles.stagedRemove, children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "close-circle", size: 20, color: "#ffffff" }) })
|
|
178
|
+
] }, `${f.uri}-${i}`))
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
function RecordingBar({
|
|
183
|
+
theme,
|
|
184
|
+
startedAt,
|
|
185
|
+
onCancel,
|
|
186
|
+
onSend
|
|
187
|
+
}) {
|
|
188
|
+
const [now, setNow] = React.useState(Date.now());
|
|
189
|
+
React.useEffect(() => {
|
|
190
|
+
const t = setInterval(() => setNow(Date.now()), 250);
|
|
191
|
+
return () => clearInterval(t);
|
|
192
|
+
}, []);
|
|
193
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.recording, {
|
|
194
|
+
flexDirection: "row",
|
|
195
|
+
backgroundColor: theme.composerBar
|
|
196
|
+
}], children: [
|
|
197
|
+
/* @__PURE__ */ jsx(Pressable, { onPress: onCancel, hitSlop: 8, accessibilityRole: "button", accessibilityLabel: "Cancel recording", style: styles.recordingButton, children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "close", size: 26, color: theme.textMuted }) }),
|
|
198
|
+
/* @__PURE__ */ jsxs(View, { style: [styles.recordingPill, {
|
|
199
|
+
flexDirection: "row",
|
|
200
|
+
backgroundColor: theme.bg,
|
|
201
|
+
borderColor: theme.border
|
|
202
|
+
}], children: [
|
|
203
|
+
/* @__PURE__ */ jsx(View, { style: [styles.recordingDot, {
|
|
204
|
+
backgroundColor: theme.danger
|
|
205
|
+
}] }),
|
|
206
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.recordingTime, {
|
|
207
|
+
color: theme.text
|
|
208
|
+
}], children: durationLabel(now - startedAt) }),
|
|
209
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.recordingHint, {
|
|
210
|
+
color: theme.textMuted
|
|
211
|
+
}], children: "Recording\u2026" })
|
|
212
|
+
] }),
|
|
213
|
+
/* @__PURE__ */ jsx(Pressable, { onPress: onSend, accessibilityRole: "button", accessibilityLabel: "Send voice message", style: [styles.recordingSend, {
|
|
214
|
+
backgroundColor: theme.accent
|
|
215
|
+
}], testID: "chat-voice-send", children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "arrow-up", size: 22, color: theme.onAccent }) })
|
|
216
|
+
] });
|
|
217
|
+
}
|
|
218
|
+
const styles = StyleSheet.create({
|
|
219
|
+
imageGrid: {
|
|
220
|
+
flexWrap: "wrap",
|
|
221
|
+
gap: 4,
|
|
222
|
+
marginVertical: 2
|
|
223
|
+
},
|
|
224
|
+
image: {
|
|
225
|
+
borderRadius: 12
|
|
226
|
+
},
|
|
227
|
+
imageOverlay: {
|
|
228
|
+
alignItems: "center",
|
|
229
|
+
justifyContent: "center",
|
|
230
|
+
backgroundColor: "rgba(0,0,0,0.25)",
|
|
231
|
+
borderRadius: 12
|
|
232
|
+
},
|
|
233
|
+
fileRow: {
|
|
234
|
+
alignItems: "center",
|
|
235
|
+
paddingVertical: 4,
|
|
236
|
+
maxWidth: 220
|
|
237
|
+
},
|
|
238
|
+
fileName: {
|
|
239
|
+
marginLeft: 6,
|
|
240
|
+
fontSize: 15,
|
|
241
|
+
flexShrink: 1
|
|
242
|
+
},
|
|
243
|
+
voice: {
|
|
244
|
+
alignItems: "center",
|
|
245
|
+
paddingVertical: 2,
|
|
246
|
+
paddingHorizontal: 6,
|
|
247
|
+
minWidth: 200
|
|
248
|
+
},
|
|
249
|
+
voiceWave: {
|
|
250
|
+
alignItems: "center",
|
|
251
|
+
flex: 1,
|
|
252
|
+
marginHorizontal: 8,
|
|
253
|
+
gap: 2
|
|
254
|
+
},
|
|
255
|
+
voiceBar: {
|
|
256
|
+
width: 3,
|
|
257
|
+
borderRadius: 2
|
|
258
|
+
},
|
|
259
|
+
voiceTime: {
|
|
260
|
+
fontSize: 13,
|
|
261
|
+
fontVariant: ["tabular-nums"]
|
|
262
|
+
},
|
|
263
|
+
viewer: {
|
|
264
|
+
flex: 1,
|
|
265
|
+
backgroundColor: "#000000"
|
|
266
|
+
},
|
|
267
|
+
viewerTop: {
|
|
268
|
+
position: "absolute",
|
|
269
|
+
top: 0,
|
|
270
|
+
left: 0,
|
|
271
|
+
right: 0,
|
|
272
|
+
paddingHorizontal: 16,
|
|
273
|
+
paddingTop: 8,
|
|
274
|
+
zIndex: 2
|
|
275
|
+
},
|
|
276
|
+
viewerClose: {
|
|
277
|
+
width: 36,
|
|
278
|
+
height: 36,
|
|
279
|
+
borderRadius: 18,
|
|
280
|
+
alignItems: "center",
|
|
281
|
+
justifyContent: "center",
|
|
282
|
+
backgroundColor: "rgba(255,255,255,0.18)"
|
|
283
|
+
},
|
|
284
|
+
sheetBackdrop: {
|
|
285
|
+
flex: 1,
|
|
286
|
+
backgroundColor: "rgba(15, 23, 42, 0.35)"
|
|
287
|
+
},
|
|
288
|
+
sheet: {
|
|
289
|
+
borderTopLeftRadius: 16,
|
|
290
|
+
borderTopRightRadius: 16,
|
|
291
|
+
paddingTop: 8,
|
|
292
|
+
paddingBottom: 8
|
|
293
|
+
},
|
|
294
|
+
sheetHandle: {
|
|
295
|
+
alignSelf: "center",
|
|
296
|
+
width: 36,
|
|
297
|
+
height: 5,
|
|
298
|
+
borderRadius: 3,
|
|
299
|
+
marginBottom: 8
|
|
300
|
+
},
|
|
301
|
+
sheetRow: {
|
|
302
|
+
alignItems: "center",
|
|
303
|
+
paddingHorizontal: 20,
|
|
304
|
+
paddingVertical: 12
|
|
305
|
+
},
|
|
306
|
+
sheetIcon: {
|
|
307
|
+
width: 40,
|
|
308
|
+
height: 40,
|
|
309
|
+
borderRadius: 20,
|
|
310
|
+
alignItems: "center",
|
|
311
|
+
justifyContent: "center"
|
|
312
|
+
},
|
|
313
|
+
sheetLabel: {
|
|
314
|
+
fontSize: 17,
|
|
315
|
+
marginLeft: 14
|
|
316
|
+
},
|
|
317
|
+
stagedBar: {
|
|
318
|
+
flexGrow: 0,
|
|
319
|
+
flexShrink: 0
|
|
320
|
+
},
|
|
321
|
+
staged: {
|
|
322
|
+
paddingHorizontal: 12,
|
|
323
|
+
paddingTop: 8,
|
|
324
|
+
gap: 8
|
|
325
|
+
},
|
|
326
|
+
stagedItem: {
|
|
327
|
+
width: 72,
|
|
328
|
+
height: 72
|
|
329
|
+
},
|
|
330
|
+
stagedImage: {
|
|
331
|
+
width: 72,
|
|
332
|
+
height: 72,
|
|
333
|
+
borderRadius: 10
|
|
334
|
+
},
|
|
335
|
+
stagedRemove: {
|
|
336
|
+
position: "absolute",
|
|
337
|
+
top: 2,
|
|
338
|
+
right: 2,
|
|
339
|
+
backgroundColor: "rgba(0,0,0,0.45)",
|
|
340
|
+
borderRadius: 10
|
|
341
|
+
},
|
|
342
|
+
recording: {
|
|
343
|
+
alignItems: "center",
|
|
344
|
+
paddingHorizontal: 8,
|
|
345
|
+
paddingVertical: 8
|
|
346
|
+
},
|
|
347
|
+
recordingButton: {
|
|
348
|
+
width: 36,
|
|
349
|
+
height: 36,
|
|
350
|
+
alignItems: "center",
|
|
351
|
+
justifyContent: "center"
|
|
352
|
+
},
|
|
353
|
+
recordingPill: {
|
|
354
|
+
flex: 1,
|
|
355
|
+
alignItems: "center",
|
|
356
|
+
minHeight: 38,
|
|
357
|
+
borderRadius: 20,
|
|
358
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
359
|
+
paddingHorizontal: 12,
|
|
360
|
+
marginHorizontal: 6
|
|
361
|
+
},
|
|
362
|
+
recordingDot: {
|
|
363
|
+
width: 10,
|
|
364
|
+
height: 10,
|
|
365
|
+
borderRadius: 5
|
|
366
|
+
},
|
|
367
|
+
recordingTime: {
|
|
368
|
+
fontSize: 16,
|
|
369
|
+
marginLeft: 8,
|
|
370
|
+
fontVariant: ["tabular-nums"]
|
|
371
|
+
},
|
|
372
|
+
recordingHint: {
|
|
373
|
+
fontSize: 15,
|
|
374
|
+
marginLeft: 10
|
|
375
|
+
},
|
|
376
|
+
recordingSend: {
|
|
377
|
+
width: 32,
|
|
378
|
+
height: 32,
|
|
379
|
+
borderRadius: 16,
|
|
380
|
+
alignItems: "center",
|
|
381
|
+
justifyContent: "center"
|
|
382
|
+
}
|
|
383
|
+
});export{AttachSheet,ImageViewer,MessageAttachments,RecordingBar,StagedAttachments,durationLabel};//# sourceMappingURL=ChatAttachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatAttachments.js","sources":["../../../src/features/messaging/ChatAttachments.tsx"],"sourcesContent":["/**\n * Attachments in the chat: photos and voice notes inside bubbles, the full-screen photo viewer,\n * and the composer pieces (the \"+\" menu, picked photos, the recording bar).\n *\n * Plain react-native + expo-image only (host-shared); picking, uploading, recording and playback\n * all go through the host seams (`ChatMediaSeam`, `ChatAudioSeam`).\n */\nimport * as React from 'react';\nimport { ActivityIndicator, Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';\nimport { Image } from 'expo-image';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';\nimport type { ChatTheme } from './ChatComponents';\nimport type { ChatAudioSeam, ChatPickedImage } from './chatHost';\nimport { attachmentUrl, type ChatAttachment } from './chatModel';\n\nconst IMAGE_MAX = 240;\n\nexport function durationLabel(ms: number | null | undefined): string {\n const total = Math.max(0, Math.round((ms ?? 0) / 1000));\n return `${Math.floor(total / 60)}:${String(total % 60).padStart(2, '0')}`;\n}\n\nfunction imageSize(a: ChatAttachment): { width: number; height: number } {\n const w = a.width ?? 0;\n const h = a.height ?? 0;\n if (!w || !h) return { width: IMAGE_MAX, height: IMAGE_MAX * 0.75 };\n const scale = Math.min(IMAGE_MAX / w, IMAGE_MAX / h);\n return { width: Math.max(120, Math.round(w * scale)), height: Math.max(90, Math.round(h * scale)) };\n}\n\n/** Photos and voice notes of one message. */\nexport function MessageAttachments({\n attachments,\n mine,\n theme,\n fileBaseUrl,\n audio,\n uploading,\n onOpenImage,\n}: {\n attachments: ChatAttachment[];\n mine: boolean;\n theme: ChatTheme;\n fileBaseUrl?: string | null;\n audio?: ChatAudioSeam;\n /** Still sending: shows a spinner over photos. */\n uploading?: boolean;\n onOpenImage?: (url: string) => void;\n}) {\n const images = attachments.filter((a) => a.kind === 'image');\n const voices = attachments.filter((a) => a.kind === 'audio');\n const others = attachments.filter((a) => a.kind === 'file');\n const textColor = mine ? theme.bubbleMineText : theme.bubbleTheirsText;\n\n return (\n <View>\n {images.length ? (\n <View style={[styles.imageGrid, { flexDirection: 'row' }]}>\n {images.map((a) => {\n const url = attachmentUrl(a, fileBaseUrl);\n const size = images.length === 1 ? imageSize(a) : { width: 116, height: 116 };\n return (\n <Pressable\n key={a.id}\n onPress={onOpenImage ? () => onOpenImage(url) : undefined}\n accessibilityRole=\"imagebutton\"\n accessibilityLabel=\"Photo\"\n >\n <Image\n source={{ uri: url }}\n style={[styles.image, size, { backgroundColor: theme.surface }]}\n contentFit=\"cover\"\n transition={120}\n />\n {uploading ? (\n <View style={[StyleSheet.absoluteFill, styles.imageOverlay]}>\n <ActivityIndicator color=\"#ffffff\" />\n </View>\n ) : null}\n </Pressable>\n );\n })}\n </View>\n ) : null}\n {voices.map((a) => (\n <VoiceNote\n key={a.id}\n url={attachmentUrl(a, fileBaseUrl)}\n durationMs={a.durationMs}\n audio={audio}\n color={textColor}\n accent={mine ? textColor : theme.accent}\n disabled={uploading}\n />\n ))}\n {others.map((a) => (\n <View key={a.id} style={[styles.fileRow, { flexDirection: 'row' }]}>\n <MaterialCommunityIcons name=\"file-outline\" size={20} color={textColor} />\n <Text numberOfLines={1} style={[styles.fileName, { color: textColor }]}>\n {a.name}\n </Text>\n </View>\n ))}\n </View>\n );\n}\n\nfunction VoiceNote({\n url,\n durationMs,\n audio,\n color,\n accent,\n disabled,\n}: {\n url: string;\n durationMs?: number | null;\n audio?: ChatAudioSeam;\n color: string;\n accent: string;\n disabled?: boolean;\n}) {\n const [playing, setPlaying] = React.useState(false);\n const stopRef = React.useRef<(() => void) | null>(null);\n\n React.useEffect(() => () => stopRef.current?.(), []);\n\n const toggle = React.useCallback(async () => {\n if (!audio) return;\n if (playing) {\n stopRef.current?.();\n stopRef.current = null;\n setPlaying(false);\n return;\n }\n setPlaying(true);\n try {\n stopRef.current = await audio.play(url, () => {\n stopRef.current = null;\n setPlaying(false);\n });\n } catch {\n setPlaying(false);\n }\n }, [audio, playing, url]);\n\n return (\n <View style={[styles.voice, { flexDirection: 'row' }]}>\n <Pressable\n onPress={() => void toggle()}\n disabled={!audio || disabled}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel={playing ? 'Pause voice message' : 'Play voice message'}\n >\n <MaterialCommunityIcons name={playing ? 'pause-circle' : 'play-circle'} size={34} color={accent} />\n </Pressable>\n <View style={[styles.voiceWave, { flexDirection: 'row' }]}>\n {WAVE.map((h, i) => (\n <View key={i} style={[styles.voiceBar, { height: h, backgroundColor: color, opacity: 0.55 }]} />\n ))}\n </View>\n <Text style={[styles.voiceTime, { color }]}>{durationLabel(durationMs)}</Text>\n </View>\n );\n}\n\nconst WAVE = [6, 12, 18, 10, 22, 14, 8, 16, 24, 12, 6, 14, 20, 10, 16, 8, 12, 18];\n\n/** Full-screen photo, tap anywhere or ✕ to close. */\nexport function ImageViewer({ url, onClose }: { url: string | null; onClose: () => void }) {\n return (\n <Modal visible={Boolean(url)} transparent animationType=\"fade\" statusBarTranslucent onRequestClose={onClose}>\n {/* A Modal is a new native root: without its own provider the safe-area insets read 0\n (first open), and the ✕ lands under the status bar where it cannot be tapped. */}\n <SafeAreaProvider>\n <View style={styles.viewer}>\n {url ? (\n <Pressable style={StyleSheet.absoluteFill} onPress={onClose} accessibilityLabel=\"Close photo\">\n <Image source={{ uri: url }} style={StyleSheet.absoluteFill} contentFit=\"contain\" />\n </Pressable>\n ) : null}\n <SafeAreaView edges={['top']} style={styles.viewerTop} pointerEvents=\"box-none\">\n <Pressable\n onPress={onClose}\n hitSlop={16}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Close\"\n style={styles.viewerClose}\n testID=\"chat-image-viewer-close\"\n >\n <MaterialCommunityIcons name=\"close\" size={24} color=\"#ffffff\" />\n </Pressable>\n </SafeAreaView>\n </View>\n </SafeAreaProvider>\n </Modal>\n );\n}\n\n/** The \"+\" menu: Photos and Camera, like iMessage's app drawer rows. */\nexport function AttachSheet({\n visible,\n theme,\n onClose,\n onPhotos,\n onCamera,\n onDismissed,\n}: {\n visible: boolean;\n theme: ChatTheme;\n onClose: () => void;\n onPhotos: () => void;\n onCamera: () => void;\n /** iOS: the sheet is fully gone - only now can the photo picker be presented. */\n onDismissed?: () => void;\n}) {\n const row = (icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'], label: string, run: () => void) => (\n <Pressable\n onPress={run}\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n style={({ pressed }) => ({ backgroundColor: pressed ? theme.surface : 'transparent' })}\n >\n <View style={[styles.sheetRow, { flexDirection: 'row' }]}>\n <View style={[styles.sheetIcon, { backgroundColor: theme.surface }]}>\n <MaterialCommunityIcons name={icon} size={22} color={theme.accent} />\n </View>\n <Text style={[styles.sheetLabel, { color: theme.text }]}>{label}</Text>\n </View>\n </Pressable>\n );\n return (\n <Modal visible={visible} transparent animationType=\"slide\" onRequestClose={onClose} onDismiss={onDismissed}>\n <Pressable style={styles.sheetBackdrop} onPress={onClose} accessibilityLabel=\"Close\" />\n <SafeAreaView edges={['bottom']} style={[styles.sheet, { backgroundColor: theme.bg }]}>\n <View style={[styles.sheetHandle, { backgroundColor: theme.border }]} />\n {row('image-multiple-outline', 'Photos', onPhotos)}\n {row('camera-outline', 'Camera', onCamera)}\n </SafeAreaView>\n </Modal>\n );\n}\n\n/** Photos picked for the next message, each removable. */\nexport function StagedAttachments({\n files,\n theme,\n onRemove,\n}: {\n files: ChatPickedImage[];\n theme: ChatTheme;\n onRemove: (index: number) => void;\n}) {\n if (!files.length) return null;\n return (\n <ScrollView\n horizontal\n showsHorizontalScrollIndicator={false}\n // ScrollView grows by default (flexGrow: 1) and would push the transcript up.\n style={[styles.stagedBar, { backgroundColor: theme.composerBar }]}\n contentContainerStyle={styles.staged}\n >\n {files.map((f, i) => (\n <View key={`${f.uri}-${i}`} style={styles.stagedItem}>\n <Image source={{ uri: f.uri }} style={styles.stagedImage} contentFit=\"cover\" />\n <Pressable\n onPress={() => onRemove(i)}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Remove photo\"\n style={styles.stagedRemove}\n >\n <MaterialCommunityIcons name=\"close-circle\" size={20} color=\"#ffffff\" />\n </Pressable>\n </View>\n ))}\n </ScrollView>\n );\n}\n\n/** Replaces the text field while recording: cancel, a live timer, send. */\nexport function RecordingBar({\n theme,\n startedAt,\n onCancel,\n onSend,\n}: {\n theme: ChatTheme;\n startedAt: number;\n onCancel: () => void;\n onSend: () => void;\n}) {\n const [now, setNow] = React.useState(Date.now());\n React.useEffect(() => {\n const t = setInterval(() => setNow(Date.now()), 250);\n return () => clearInterval(t);\n }, []);\n return (\n <View style={[styles.recording, { flexDirection: 'row', backgroundColor: theme.composerBar }]}>\n <Pressable\n onPress={onCancel}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Cancel recording\"\n style={styles.recordingButton}\n >\n <MaterialCommunityIcons name=\"close\" size={26} color={theme.textMuted} />\n </Pressable>\n <View\n style={[\n styles.recordingPill,\n { flexDirection: 'row', backgroundColor: theme.bg, borderColor: theme.border },\n ]}\n >\n <View style={[styles.recordingDot, { backgroundColor: theme.danger }]} />\n <Text style={[styles.recordingTime, { color: theme.text }]}>{durationLabel(now - startedAt)}</Text>\n <Text style={[styles.recordingHint, { color: theme.textMuted }]}>Recording…</Text>\n </View>\n <Pressable\n onPress={onSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send voice message\"\n style={[styles.recordingSend, { backgroundColor: theme.accent }]}\n testID=\"chat-voice-send\"\n >\n <MaterialCommunityIcons name=\"arrow-up\" size={22} color={theme.onAccent} />\n </Pressable>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n imageGrid: { flexWrap: 'wrap', gap: 4, marginVertical: 2 },\n image: { borderRadius: 12 },\n imageOverlay: {\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'rgba(0,0,0,0.25)',\n borderRadius: 12,\n },\n fileRow: { alignItems: 'center', paddingVertical: 4, maxWidth: 220 },\n fileName: { marginLeft: 6, fontSize: 15, flexShrink: 1 },\n voice: { alignItems: 'center', paddingVertical: 2, paddingHorizontal: 6, minWidth: 200 },\n voiceWave: { alignItems: 'center', flex: 1, marginHorizontal: 8, gap: 2 },\n voiceBar: { width: 3, borderRadius: 2 },\n voiceTime: { fontSize: 13, fontVariant: ['tabular-nums'] },\n viewer: { flex: 1, backgroundColor: '#000000' },\n viewerTop: { position: 'absolute', top: 0, left: 0, right: 0, paddingHorizontal: 16, paddingTop: 8, zIndex: 2 },\n viewerClose: {\n width: 36,\n height: 36,\n borderRadius: 18,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'rgba(255,255,255,0.18)',\n },\n sheetBackdrop: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.35)' },\n sheet: { borderTopLeftRadius: 16, borderTopRightRadius: 16, paddingTop: 8, paddingBottom: 8 },\n sheetHandle: { alignSelf: 'center', width: 36, height: 5, borderRadius: 3, marginBottom: 8 },\n sheetRow: { alignItems: 'center', paddingHorizontal: 20, paddingVertical: 12 },\n sheetIcon: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },\n sheetLabel: { fontSize: 17, marginLeft: 14 },\n stagedBar: { flexGrow: 0, flexShrink: 0 },\n staged: { paddingHorizontal: 12, paddingTop: 8, gap: 8 },\n stagedItem: { width: 72, height: 72 },\n stagedImage: { width: 72, height: 72, borderRadius: 10 },\n stagedRemove: { position: 'absolute', top: 2, right: 2, backgroundColor: 'rgba(0,0,0,0.45)', borderRadius: 10 },\n recording: { alignItems: 'center', paddingHorizontal: 8, paddingVertical: 8 },\n recordingButton: { width: 36, height: 36, alignItems: 'center', justifyContent: 'center' },\n recordingPill: {\n flex: 1,\n alignItems: 'center',\n minHeight: 38,\n borderRadius: 20,\n borderWidth: StyleSheet.hairlineWidth,\n paddingHorizontal: 12,\n marginHorizontal: 6,\n },\n recordingDot: { width: 10, height: 10, borderRadius: 5 },\n recordingTime: { fontSize: 16, marginLeft: 8, fontVariant: ['tabular-nums'] },\n recordingHint: { fontSize: 15, marginLeft: 10 },\n recordingSend: { width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center' },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsxs,jsx}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,View,Text,Pressable}from'react-native';import {Image}from'expo-image';import {MaterialCommunityIcons}from'@expo/vector-icons';import {mobileColors}from'../../theme/mobileTokens.js';import {initialsOf,timeLabel,relativeLabel}from'./chatModel.js';function buildChatTheme(isDark) {
|
|
1
|
+
import {jsxs,jsx}from'react/jsx-runtime';import*as React from'react';import {StyleSheet,View,Text,Pressable}from'react-native';import {Image}from'expo-image';import {MaterialCommunityIcons}from'@expo/vector-icons';import {mobileColors}from'../../theme/mobileTokens.js';import {initialsOf,timeLabel,relativeLabel}from'./chatModel.js';import {MessageAttachments}from'./ChatAttachments.js';function buildChatTheme(isDark) {
|
|
2
2
|
const c = mobileColors(isDark);
|
|
3
3
|
return {
|
|
4
4
|
bg: c.bg,
|
|
@@ -150,7 +150,10 @@ function MessageBubble({
|
|
|
150
150
|
groupWithPrevious,
|
|
151
151
|
groupWithNext,
|
|
152
152
|
onRetry,
|
|
153
|
-
onLongPress
|
|
153
|
+
onLongPress,
|
|
154
|
+
fileBaseUrl,
|
|
155
|
+
audio,
|
|
156
|
+
onOpenImage
|
|
154
157
|
}) {
|
|
155
158
|
var _a;
|
|
156
159
|
const bubbleRef = React.useRef(null);
|
|
@@ -198,14 +201,17 @@ function MessageBubble({
|
|
|
198
201
|
height
|
|
199
202
|
}));
|
|
200
203
|
} : void 0, delayLongPress: 280, disabled: !onLongPress, accessibilityHint: onLongPress ? "Long press for reactions and options" : void 0, style: [styles.bubblePress, reactions.length ? styles.bubbleReacted : null], children: [
|
|
201
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ jsxs(View, { ref: bubbleRef, collapsable: false, style: [styles.bubble, styles.bubbleFill, radii, {
|
|
202
205
|
backgroundColor: mine ? theme.bubbleMine : theme.bubbleTheirs
|
|
203
206
|
}, failed && {
|
|
204
207
|
borderWidth: 1,
|
|
205
208
|
borderColor: theme.danger
|
|
206
|
-
}, sending && styles.bubbleSending
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
}, sending && !message.attachments.length && styles.bubbleSending, message.attachments.length ? styles.bubbleWithMedia : null], children: [
|
|
210
|
+
message.attachments.length ? /* @__PURE__ */ jsx(MessageAttachments, { attachments: message.attachments, mine, theme, fileBaseUrl, audio, uploading: sending, onOpenImage }) : null,
|
|
211
|
+
message.body.trim() ? /* @__PURE__ */ jsx(Text, { style: [styles.bubbleText, {
|
|
212
|
+
color: mine ? theme.bubbleMineText : theme.bubbleTheirsText
|
|
213
|
+
}, message.attachments.length ? styles.bubbleCaption : null], children: message.body.trim() }) : null
|
|
214
|
+
] }),
|
|
209
215
|
reactions.length ? (
|
|
210
216
|
// iMessage puts the Tapback on the bubble's top corner facing the other person.
|
|
211
217
|
/* @__PURE__ */ jsx(View, { style: [styles.reactionBadge, mine ? styles.reactionBadgeMine : styles.reactionBadgeTheirs, {
|
|
@@ -449,6 +455,16 @@ const styles = StyleSheet.create({
|
|
|
449
455
|
fontSize: 17,
|
|
450
456
|
lineHeight: 22
|
|
451
457
|
},
|
|
458
|
+
/** Photos sit close to the bubble edge, like iMessage. */
|
|
459
|
+
bubbleWithMedia: {
|
|
460
|
+
paddingHorizontal: 4,
|
|
461
|
+
paddingVertical: 4
|
|
462
|
+
},
|
|
463
|
+
bubbleCaption: {
|
|
464
|
+
paddingHorizontal: 8,
|
|
465
|
+
paddingTop: 4,
|
|
466
|
+
paddingBottom: 2
|
|
467
|
+
},
|
|
452
468
|
timestampRow: {
|
|
453
469
|
flexDirection: "row",
|
|
454
470
|
alignItems: "center",
|