valtech-components 4.0.230 → 4.0.231
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/esm2022/lib/components/molecules/chat-composer/chat-composer.component.mjs +107 -300
- package/esm2022/lib/components/molecules/message-bubble/message-bubble.component.mjs +4 -19
- package/esm2022/lib/components/organisms/chat-window/chat-window.component.mjs +8 -5
- package/esm2022/lib/components/organisms/thread-panel/thread-panel.component.mjs +4 -6
- package/esm2022/lib/services/chat/conversation.service.mjs +4 -15
- package/esm2022/lib/services/pdf/pdf.service.mjs +44 -4
- package/esm2022/lib/services/pdf/types.mjs +1 -1
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +2 -2
- package/fesm2022/valtech-components.mjs +162 -342
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/chat-composer/chat-composer.component.d.ts +15 -44
- package/lib/components/molecules/message-bubble/message-bubble.component.d.ts +0 -1
- package/lib/components/organisms/chat-window/chat-window.component.d.ts +4 -4
- package/lib/components/organisms/thread-panel/thread-panel.component.d.ts +1 -4
- package/lib/services/chat/conversation.service.d.ts +3 -5
- package/lib/services/pdf/pdf.service.d.ts +25 -3
- package/lib/services/pdf/types.d.ts +25 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +1 -1
|
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
|
|
|
56
56
|
* Current version of valtech-components.
|
|
57
57
|
* This is automatically updated during the publish process.
|
|
58
58
|
*/
|
|
59
|
-
const VERSION = '4.0.
|
|
59
|
+
const VERSION = '4.0.231';
|
|
60
60
|
|
|
61
61
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
62
62
|
let isRefreshing = false;
|
|
@@ -61830,12 +61830,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
61830
61830
|
}] } });
|
|
61831
61831
|
|
|
61832
61832
|
/**
|
|
61833
|
-
* Servicio
|
|
61833
|
+
* Servicio de PDF de plataforma (ADR-042). Reutilizable por cualquier app.
|
|
61834
61834
|
*
|
|
61835
61835
|
* El JWT del usuario se inyecta automáticamente por el authInterceptor
|
|
61836
61836
|
* (configurado vía provideValtechAuth). El app-id se toma de ValtechAuthConfig.
|
|
61837
61837
|
*
|
|
61838
|
-
*
|
|
61838
|
+
* Tres modos:
|
|
61839
|
+
* - generate() → HTTP sync: genera y devuelve la URL presignada.
|
|
61840
|
+
* - createJob() → async: encola un job (delivery.mode='async') y devuelve
|
|
61841
|
+
* { jobId, status:'queued' }. El PDF se genera fuera de banda.
|
|
61842
|
+
* - watchJob() → observa el estado del job en tiempo real (Firestore),
|
|
61843
|
+
* proyectado por el backend al completarse.
|
|
61844
|
+
*
|
|
61845
|
+
* Fuentes (en los 3 modos):
|
|
61839
61846
|
* - source.template → renderiza una plantilla desde DynamoDB
|
|
61840
61847
|
* - source.html → HTML arbitrario provisto por el caller
|
|
61841
61848
|
* - source.url → URL pública (Puppeteer la carga)
|
|
@@ -61844,6 +61851,8 @@ class PdfService {
|
|
|
61844
61851
|
constructor(config) {
|
|
61845
61852
|
this.config = config;
|
|
61846
61853
|
this.http = inject(HttpClient);
|
|
61854
|
+
this.auth = inject(AuthService);
|
|
61855
|
+
this.collections = inject(FirestoreCollectionFactory);
|
|
61847
61856
|
}
|
|
61848
61857
|
get baseUrl() {
|
|
61849
61858
|
return `${this.config?.apiUrl ?? ''}/v2/pdf`;
|
|
@@ -61860,6 +61869,35 @@ class PdfService {
|
|
|
61860
61869
|
generateAndDownload(req, filename = 'documento.pdf') {
|
|
61861
61870
|
return this.generate(req).pipe(switchMap$1(result => from(this._download(result.url, filename))));
|
|
61862
61871
|
}
|
|
61872
|
+
/**
|
|
61873
|
+
* Encola un job PDF async. El backend publica `pdf.generate.requested`; el PDF
|
|
61874
|
+
* Lambda lo genera fuera de banda y, al completar, proyecta el estado a
|
|
61875
|
+
* Firestore. Usar watchJob(jobId) para observar el resultado en tiempo real.
|
|
61876
|
+
*/
|
|
61877
|
+
createJob(req) {
|
|
61878
|
+
const body = {
|
|
61879
|
+
...req,
|
|
61880
|
+
delivery: { ...(req.delivery ?? {}), mode: 'async' },
|
|
61881
|
+
};
|
|
61882
|
+
return this.http.post(`${this.baseUrl}/generate`, body, {
|
|
61883
|
+
headers: this.appIdHeader,
|
|
61884
|
+
});
|
|
61885
|
+
}
|
|
61886
|
+
/**
|
|
61887
|
+
* Observa el estado de un job PDF async en tiempo real (onSnapshot del doc
|
|
61888
|
+
* `apps/{appId}/orgs/{orgId}/pdf-jobs/{jobId}`). Emite null si no hay org
|
|
61889
|
+
* activa o hasta que el doc exista. Cuando el job completa/falla, el doc trae
|
|
61890
|
+
* status + url. Cierra la suscripción al completar tu propia lógica.
|
|
61891
|
+
*/
|
|
61892
|
+
watchJob(jobId) {
|
|
61893
|
+
const orgId = this.auth.user()?.activeOrg ?? '';
|
|
61894
|
+
if (!orgId || !jobId)
|
|
61895
|
+
return of(null);
|
|
61896
|
+
const coll = this.collections.create(`orgs/${orgId}/pdf-jobs`, {
|
|
61897
|
+
timestamps: false,
|
|
61898
|
+
});
|
|
61899
|
+
return coll.watch(jobId);
|
|
61900
|
+
}
|
|
61863
61901
|
async _download(url, _filename) {
|
|
61864
61902
|
window.open(url, '_blank', 'noopener');
|
|
61865
61903
|
}
|
|
@@ -72171,21 +72209,10 @@ class ConversationService {
|
|
|
72171
72209
|
return this.http.delete(`${this.baseUrl}/conversations/${convId}/participants/${userId}`);
|
|
72172
72210
|
}
|
|
72173
72211
|
/**
|
|
72174
|
-
* Envia un mensaje a una conversacion
|
|
72175
|
-
* El backend espera `attachments: { url, type, name, size }[]` (key `type` = MIME),
|
|
72176
|
-
* mientras que el front usa `mimeType` — se mapea aqui.
|
|
72212
|
+
* Envia un mensaje a una conversacion.
|
|
72177
72213
|
*/
|
|
72178
|
-
sendMessage(convId, body
|
|
72179
|
-
|
|
72180
|
-
if (attachments?.length) {
|
|
72181
|
-
payload['attachments'] = attachments.map(a => ({
|
|
72182
|
-
url: a.url,
|
|
72183
|
-
type: a.mimeType,
|
|
72184
|
-
name: a.name,
|
|
72185
|
-
size: a.size ?? 0,
|
|
72186
|
-
}));
|
|
72187
|
-
}
|
|
72188
|
-
return this.http.post(`${this.baseUrl}/conversations/${convId}/messages`, payload);
|
|
72214
|
+
sendMessage(convId, body) {
|
|
72215
|
+
return this.http.post(`${this.baseUrl}/conversations/${convId}/messages`, { body });
|
|
72189
72216
|
}
|
|
72190
72217
|
/**
|
|
72191
72218
|
* Elimina (marca como eliminado) un mensaje.
|
|
@@ -72412,8 +72439,7 @@ class MessageBubbleComponent {
|
|
|
72412
72439
|
return (parts[0][0] + (parts[1]?.[0] ?? '')).toUpperCase();
|
|
72413
72440
|
});
|
|
72414
72441
|
this.imageAttachments = computed(() => (this.msg().attachments ?? []).filter(a => a.mimeType?.startsWith('image/')));
|
|
72415
|
-
this.
|
|
72416
|
-
this.fileAttachments = computed(() => (this.msg().attachments ?? []).filter(a => !a.mimeType?.startsWith('image/') && !a.mimeType?.startsWith('audio/')));
|
|
72442
|
+
this.fileAttachments = computed(() => (this.msg().attachments ?? []).filter(a => !a.mimeType?.startsWith('image/')));
|
|
72417
72443
|
this.statusIcon = computed(() => {
|
|
72418
72444
|
switch (this.msg().status) {
|
|
72419
72445
|
case 'sending':
|
|
@@ -72484,13 +72510,6 @@ class MessageBubbleComponent {
|
|
|
72484
72510
|
}
|
|
72485
72511
|
</div>
|
|
72486
72512
|
}
|
|
72487
|
-
@if (audioAttachments().length > 0) {
|
|
72488
|
-
<div class="audios">
|
|
72489
|
-
@for (att of audioAttachments(); track att.url) {
|
|
72490
|
-
<audio class="att-audio" controls preload="metadata" [src]="att.url"></audio>
|
|
72491
|
-
}
|
|
72492
|
-
</div>
|
|
72493
|
-
}
|
|
72494
72513
|
@if (fileAttachments().length > 0) {
|
|
72495
72514
|
<div class="files">
|
|
72496
72515
|
@for (att of fileAttachments(); track att.url) {
|
|
@@ -72554,7 +72573,7 @@ class MessageBubbleComponent {
|
|
|
72554
72573
|
}
|
|
72555
72574
|
</div>
|
|
72556
72575
|
</div>
|
|
72557
|
-
`, isInline: true, styles: [":host{display:block}.row{display:flex;align-items:flex-end;gap:8px;margin:1px 0}.row.mine{flex-direction:row-reverse}.row.tail{margin-bottom:8px}.avatar{width:28px;height:28px;border-radius:50%;overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--ion-color-primary);color:#fff;font-size:.6875rem;font-weight:700}.avatar.hidden{visibility:hidden}.avatar img{width:100%;height:100%;object-fit:cover}.bubble-wrap{position:relative;max-width:min(78%,520px);display:flex;flex-direction:column}.row.mine .bubble-wrap{align-items:flex-end}.bubble{position:relative;padding:7px 11px;border-radius:16px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));color:var(--ion-text-color, #000);font-size:.9375rem;line-height:1.35;cursor:pointer;outline:none;word-break:break-word}.row:not(.mine).tail .bubble{border-bottom-left-radius:5px}.mine .bubble{background:var(--ion-color-primary);border-color:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff)}.mine.tail .bubble{border-bottom-right-radius:5px}.bubble.deleted{background:transparent;border-style:dashed;cursor:default}.name{display:block;font-size:.75rem;font-weight:700;color:var(--ion-color-primary);margin-bottom:2px}.reply-preview{display:flex;flex-direction:column;gap:1px;padding:4px 8px;margin-bottom:4px;border-left:3px solid currentColor;border-radius:6px;background:var(--ion-color-step-100, rgba(127, 127, 127, .12));font-size:.8125rem;opacity:.85}.reply-name{font-weight:700}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px}.body{white-space:pre-wrap}.deleted-text{font-style:italic;opacity:.6}.attachments{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.att-image{max-width:100%;border-radius:10px;display:block}.
|
|
72576
|
+
`, isInline: true, styles: [":host{display:block}.row{display:flex;align-items:flex-end;gap:8px;margin:1px 0}.row.mine{flex-direction:row-reverse}.row.tail{margin-bottom:8px}.avatar{width:28px;height:28px;border-radius:50%;overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--ion-color-primary);color:#fff;font-size:.6875rem;font-weight:700}.avatar.hidden{visibility:hidden}.avatar img{width:100%;height:100%;object-fit:cover}.bubble-wrap{position:relative;max-width:min(78%,520px);display:flex;flex-direction:column}.row.mine .bubble-wrap{align-items:flex-end}.bubble{position:relative;padding:7px 11px;border-radius:16px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));color:var(--ion-text-color, #000);font-size:.9375rem;line-height:1.35;cursor:pointer;outline:none;word-break:break-word}.row:not(.mine).tail .bubble{border-bottom-left-radius:5px}.mine .bubble{background:var(--ion-color-primary);border-color:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff)}.mine.tail .bubble{border-bottom-right-radius:5px}.bubble.deleted{background:transparent;border-style:dashed;cursor:default}.name{display:block;font-size:.75rem;font-weight:700;color:var(--ion-color-primary);margin-bottom:2px}.reply-preview{display:flex;flex-direction:column;gap:1px;padding:4px 8px;margin-bottom:4px;border-left:3px solid currentColor;border-radius:6px;background:var(--ion-color-step-100, rgba(127, 127, 127, .12));font-size:.8125rem;opacity:.85}.reply-name{font-weight:700}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px}.body{white-space:pre-wrap}.deleted-text{font-style:italic;opacity:.6}.attachments{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.att-image{max-width:100%;border-radius:10px;display:block}.files{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.file-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .12));color:inherit;text-decoration:none;font-size:.8125rem}.meta{display:inline-flex;align-items:center;gap:4px;float:right;margin:2px 0 -2px 8px;font-size:.6875rem;opacity:.7}.edited{font-style:italic}.status{font-size:.875rem}.status.read{color:var(--ion-color-secondary, #4fc3f7);opacity:1}.status.failed{color:var(--ion-color-danger, #eb445a);opacity:1}.reactions{display:flex;flex-wrap:wrap;gap:4px;margin-top:3px}.reaction{display:inline-flex;align-items:center;gap:3px;padding:1px 7px;border-radius:999px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));font-size:.75rem;cursor:pointer;color:var(--ion-text-color, #000)}.reaction.active{border-color:var(--ion-color-primary);background:var(--ion-color-primary-tint, var(--ion-color-primary));color:var(--ion-color-primary-contrast, #fff)}.reaction .count{opacity:.75}.actions{position:absolute;top:-14px;display:flex;gap:2px;padding:2px;border-radius:999px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));box-shadow:0 2px 8px #0000001f;opacity:0;pointer-events:none;transition:opacity .12s ease;z-index:2}.row.mine .actions{right:0}.row:not(.mine) .actions{left:0}.bubble-wrap:hover .actions,.actions.open{opacity:1;pointer-events:auto}.act{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1rem}.act:hover{background:var(--ion-color-step-100, rgba(127, 127, 127, .12));color:var(--ion-text-color, #000)}.act.danger:hover{color:var(--ion-color-danger, #eb445a)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
|
|
72558
72577
|
}
|
|
72559
72578
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageBubbleComponent, decorators: [{
|
|
72560
72579
|
type: Component,
|
|
@@ -72599,13 +72618,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
72599
72618
|
}
|
|
72600
72619
|
</div>
|
|
72601
72620
|
}
|
|
72602
|
-
@if (audioAttachments().length > 0) {
|
|
72603
|
-
<div class="audios">
|
|
72604
|
-
@for (att of audioAttachments(); track att.url) {
|
|
72605
|
-
<audio class="att-audio" controls preload="metadata" [src]="att.url"></audio>
|
|
72606
|
-
}
|
|
72607
|
-
</div>
|
|
72608
|
-
}
|
|
72609
72621
|
@if (fileAttachments().length > 0) {
|
|
72610
72622
|
<div class="files">
|
|
72611
72623
|
@for (att of fileAttachments(); track att.url) {
|
|
@@ -72669,7 +72681,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
72669
72681
|
}
|
|
72670
72682
|
</div>
|
|
72671
72683
|
</div>
|
|
72672
|
-
`, styles: [":host{display:block}.row{display:flex;align-items:flex-end;gap:8px;margin:1px 0}.row.mine{flex-direction:row-reverse}.row.tail{margin-bottom:8px}.avatar{width:28px;height:28px;border-radius:50%;overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--ion-color-primary);color:#fff;font-size:.6875rem;font-weight:700}.avatar.hidden{visibility:hidden}.avatar img{width:100%;height:100%;object-fit:cover}.bubble-wrap{position:relative;max-width:min(78%,520px);display:flex;flex-direction:column}.row.mine .bubble-wrap{align-items:flex-end}.bubble{position:relative;padding:7px 11px;border-radius:16px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));color:var(--ion-text-color, #000);font-size:.9375rem;line-height:1.35;cursor:pointer;outline:none;word-break:break-word}.row:not(.mine).tail .bubble{border-bottom-left-radius:5px}.mine .bubble{background:var(--ion-color-primary);border-color:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff)}.mine.tail .bubble{border-bottom-right-radius:5px}.bubble.deleted{background:transparent;border-style:dashed;cursor:default}.name{display:block;font-size:.75rem;font-weight:700;color:var(--ion-color-primary);margin-bottom:2px}.reply-preview{display:flex;flex-direction:column;gap:1px;padding:4px 8px;margin-bottom:4px;border-left:3px solid currentColor;border-radius:6px;background:var(--ion-color-step-100, rgba(127, 127, 127, .12));font-size:.8125rem;opacity:.85}.reply-name{font-weight:700}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px}.body{white-space:pre-wrap}.deleted-text{font-style:italic;opacity:.6}.attachments{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.att-image{max-width:100%;border-radius:10px;display:block}.
|
|
72684
|
+
`, styles: [":host{display:block}.row{display:flex;align-items:flex-end;gap:8px;margin:1px 0}.row.mine{flex-direction:row-reverse}.row.tail{margin-bottom:8px}.avatar{width:28px;height:28px;border-radius:50%;overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--ion-color-primary);color:#fff;font-size:.6875rem;font-weight:700}.avatar.hidden{visibility:hidden}.avatar img{width:100%;height:100%;object-fit:cover}.bubble-wrap{position:relative;max-width:min(78%,520px);display:flex;flex-direction:column}.row.mine .bubble-wrap{align-items:flex-end}.bubble{position:relative;padding:7px 11px;border-radius:16px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));color:var(--ion-text-color, #000);font-size:.9375rem;line-height:1.35;cursor:pointer;outline:none;word-break:break-word}.row:not(.mine).tail .bubble{border-bottom-left-radius:5px}.mine .bubble{background:var(--ion-color-primary);border-color:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff)}.mine.tail .bubble{border-bottom-right-radius:5px}.bubble.deleted{background:transparent;border-style:dashed;cursor:default}.name{display:block;font-size:.75rem;font-weight:700;color:var(--ion-color-primary);margin-bottom:2px}.reply-preview{display:flex;flex-direction:column;gap:1px;padding:4px 8px;margin-bottom:4px;border-left:3px solid currentColor;border-radius:6px;background:var(--ion-color-step-100, rgba(127, 127, 127, .12));font-size:.8125rem;opacity:.85}.reply-name{font-weight:700}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px}.body{white-space:pre-wrap}.deleted-text{font-style:italic;opacity:.6}.attachments{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.att-image{max-width:100%;border-radius:10px;display:block}.files{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.file-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .12));color:inherit;text-decoration:none;font-size:.8125rem}.meta{display:inline-flex;align-items:center;gap:4px;float:right;margin:2px 0 -2px 8px;font-size:.6875rem;opacity:.7}.edited{font-style:italic}.status{font-size:.875rem}.status.read{color:var(--ion-color-secondary, #4fc3f7);opacity:1}.status.failed{color:var(--ion-color-danger, #eb445a);opacity:1}.reactions{display:flex;flex-wrap:wrap;gap:4px;margin-top:3px}.reaction{display:inline-flex;align-items:center;gap:3px;padding:1px 7px;border-radius:999px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));font-size:.75rem;cursor:pointer;color:var(--ion-text-color, #000)}.reaction.active{border-color:var(--ion-color-primary);background:var(--ion-color-primary-tint, var(--ion-color-primary));color:var(--ion-color-primary-contrast, #fff)}.reaction .count{opacity:.75}.actions{position:absolute;top:-14px;display:flex;gap:2px;padding:2px;border-radius:999px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));box-shadow:0 2px 8px #0000001f;opacity:0;pointer-events:none;transition:opacity .12s ease;z-index:2}.row.mine .actions{right:0}.row:not(.mine) .actions{left:0}.bubble-wrap:hover .actions,.actions.open{opacity:1;pointer-events:auto}.act{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1rem}.act:hover{background:var(--ion-color-step-100, rgba(127, 127, 127, .12));color:var(--ion-text-color, #000)}.act.danger:hover{color:var(--ion-color-danger, #eb445a)}\n"] }]
|
|
72673
72685
|
}], ctorParameters: () => [] });
|
|
72674
72686
|
|
|
72675
72687
|
addIcons({ sendOutline });
|
|
@@ -72822,37 +72834,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
72822
72834
|
type: Output
|
|
72823
72835
|
}] } });
|
|
72824
72836
|
|
|
72825
|
-
addIcons({ addOutline,
|
|
72837
|
+
addIcons({ addOutline, closeOutline, happyOutline, micOutline, arrowUpOutline });
|
|
72826
72838
|
const CHAT_COMPOSER_I18N = {
|
|
72827
72839
|
es: {
|
|
72828
72840
|
placeholder: 'Escribe un mensaje',
|
|
72829
72841
|
send: 'Enviar',
|
|
72830
72842
|
attach: 'Adjuntar',
|
|
72843
|
+
emoji: 'Emoji',
|
|
72831
72844
|
voice: 'Mensaje de voz',
|
|
72832
|
-
done: 'Listo',
|
|
72833
72845
|
replyingTo: 'Respondiendo a',
|
|
72834
72846
|
cancel: 'Cancelar',
|
|
72835
|
-
remove: 'Quitar',
|
|
72836
72847
|
},
|
|
72837
72848
|
en: {
|
|
72838
72849
|
placeholder: 'Write a message',
|
|
72839
72850
|
send: 'Send',
|
|
72840
72851
|
attach: 'Attach',
|
|
72852
|
+
emoji: 'Emoji',
|
|
72841
72853
|
voice: 'Voice message',
|
|
72842
|
-
done: 'Done',
|
|
72843
72854
|
replyingTo: 'Replying to',
|
|
72844
72855
|
cancel: 'Cancel',
|
|
72845
|
-
remove: 'Remove',
|
|
72846
72856
|
},
|
|
72847
72857
|
};
|
|
72858
|
+
const QUICK_EMOJIS = ['👍', '❤️', '😂', '🎉', '🙏', '🔥', '😮', '😢'];
|
|
72848
72859
|
/**
|
|
72849
72860
|
* val-chat-composer — barra de redacción de chat moderna (signal inputs).
|
|
72850
72861
|
*
|
|
72851
|
-
* Pill con: adjuntar (+), textarea auto-grow, y mic/enviar
|
|
72852
|
-
*
|
|
72853
|
-
*
|
|
72854
|
-
*
|
|
72855
|
-
* No incluye picker de emoji: el teclado del sistema ya lo trae.
|
|
72862
|
+
* Pill con: adjuntar (+), textarea auto-grow, emoji (fila rápida) y mic/enviar
|
|
72863
|
+
* (mic cuando está vacío, enviar cuando hay texto — patrón WhatsApp/Telegram).
|
|
72864
|
+
* Muestra una barra de contexto "respondiendo a" cuando `replyingTo` está seteado.
|
|
72865
|
+
* `typing` se emite con debounce (máx. 1 vez cada 2s) para no spamear writes.
|
|
72856
72866
|
*/
|
|
72857
72867
|
class ChatComposerComponent {
|
|
72858
72868
|
constructor() {
|
|
@@ -72862,30 +72872,20 @@ class ChatComposerComponent {
|
|
|
72862
72872
|
this.maxLength = input(2000);
|
|
72863
72873
|
this.replyingTo = input(null);
|
|
72864
72874
|
this.showAttach = input(true);
|
|
72875
|
+
this.showEmoji = input(true);
|
|
72865
72876
|
this.showMic = input(true);
|
|
72866
72877
|
this.send = output();
|
|
72867
72878
|
this.typing = output();
|
|
72879
|
+
this.attach = output();
|
|
72868
72880
|
this.voice = output();
|
|
72869
72881
|
this.cancelReply = output();
|
|
72870
72882
|
this.body = signal('');
|
|
72871
|
-
this.
|
|
72872
|
-
this.
|
|
72873
|
-
// Grabación de audio (MediaRecorder).
|
|
72874
|
-
this.recording = signal(false);
|
|
72875
|
-
this.recSeconds = signal(0);
|
|
72876
|
-
this.recChunks = [];
|
|
72877
|
-
this.recTimeLabel = computed(() => {
|
|
72878
|
-
const s = this.recSeconds();
|
|
72879
|
-
const mm = String(Math.floor(s / 60)).padStart(2, '0');
|
|
72880
|
-
const ss = String(s % 60).padStart(2, '0');
|
|
72881
|
-
return `${mm}:${ss}`;
|
|
72882
|
-
});
|
|
72883
|
+
this.emojiOpen = signal(false);
|
|
72884
|
+
this.quickEmojis = QUICK_EMOJIS;
|
|
72883
72885
|
this.lastTypingEmit = 0;
|
|
72884
72886
|
this.canSend = computed(() => {
|
|
72885
72887
|
const t = this.body().trim();
|
|
72886
|
-
|
|
72887
|
-
return false;
|
|
72888
|
-
return (t.length > 0 && t.length <= this.maxLength()) || this.pending().length > 0;
|
|
72888
|
+
return t.length > 0 && t.length <= this.maxLength() && !this.disabled();
|
|
72889
72889
|
});
|
|
72890
72890
|
this.placeholderText = computed(() => {
|
|
72891
72891
|
this.i18n.lang();
|
|
@@ -72905,62 +72905,29 @@ class ChatComposerComponent {
|
|
|
72905
72905
|
}
|
|
72906
72906
|
}
|
|
72907
72907
|
onSend() {
|
|
72908
|
-
|
|
72908
|
+
const trimmed = this.body().trim();
|
|
72909
|
+
if (!trimmed || trimmed.length > this.maxLength())
|
|
72909
72910
|
return;
|
|
72910
|
-
|
|
72911
|
-
// los sube y manda como UN mensaje con body + attachments.
|
|
72912
|
-
const files = this.pending().map(p => p.file);
|
|
72913
|
-
const text = this.body().trim();
|
|
72914
|
-
this.send.emit({ text, files });
|
|
72915
|
-
for (const att of this.pending()) {
|
|
72916
|
-
if (att.url)
|
|
72917
|
-
URL.revokeObjectURL(att.url);
|
|
72918
|
-
}
|
|
72919
|
-
this.pending.set([]);
|
|
72911
|
+
this.send.emit(trimmed);
|
|
72920
72912
|
this.body.set('');
|
|
72913
|
+
this.emojiOpen.set(false);
|
|
72914
|
+
}
|
|
72915
|
+
toggleEmoji() {
|
|
72916
|
+
this.emojiOpen.update(v => !v);
|
|
72917
|
+
}
|
|
72918
|
+
insertEmoji(emoji) {
|
|
72919
|
+
this.body.update(v => v + emoji);
|
|
72921
72920
|
}
|
|
72922
72921
|
onFile(event) {
|
|
72923
72922
|
const input = event.target;
|
|
72924
72923
|
const file = input.files?.[0];
|
|
72925
72924
|
if (file)
|
|
72926
|
-
this.
|
|
72925
|
+
this.attach.emit(file);
|
|
72927
72926
|
input.value = '';
|
|
72928
72927
|
}
|
|
72929
|
-
/** Pegar imagen desde el portapapeles (captura de pantalla, copiar imagen). */
|
|
72930
|
-
onPaste(event) {
|
|
72931
|
-
const items = event.clipboardData?.items;
|
|
72932
|
-
if (!items)
|
|
72933
|
-
return;
|
|
72934
|
-
for (const item of Array.from(items)) {
|
|
72935
|
-
if (item.type.startsWith('image/')) {
|
|
72936
|
-
const file = item.getAsFile();
|
|
72937
|
-
if (file) {
|
|
72938
|
-
event.preventDefault();
|
|
72939
|
-
this.addPending(file);
|
|
72940
|
-
}
|
|
72941
|
-
return;
|
|
72942
|
-
}
|
|
72943
|
-
}
|
|
72944
|
-
}
|
|
72945
|
-
addPending(file) {
|
|
72946
|
-
const kind = file.type.startsWith('image/')
|
|
72947
|
-
? 'image'
|
|
72948
|
-
: file.type.startsWith('audio/')
|
|
72949
|
-
? 'audio'
|
|
72950
|
-
: 'file';
|
|
72951
|
-
const url = kind === 'file' ? undefined : URL.createObjectURL(file);
|
|
72952
|
-
this.pending.update(arr => [...arr, { id: this.pendingId++, file, url, kind }]);
|
|
72953
|
-
}
|
|
72954
|
-
removePending(id) {
|
|
72955
|
-
this.pending.update(arr => {
|
|
72956
|
-
const found = arr.find(a => a.id === id);
|
|
72957
|
-
if (found?.url)
|
|
72958
|
-
URL.revokeObjectURL(found.url);
|
|
72959
|
-
return arr.filter(a => a.id !== id);
|
|
72960
|
-
});
|
|
72961
|
-
}
|
|
72962
72928
|
/**
|
|
72963
|
-
* Emite typing como máximo 1 vez cada 2s.
|
|
72929
|
+
* Emite typing como máximo 1 vez cada 2s. No usa Date.now() directo en plantillas
|
|
72930
|
+
* de workflow; aquí estamos en runtime de navegador, performance.now es seguro.
|
|
72964
72931
|
*/
|
|
72965
72932
|
emitTypingDebounced() {
|
|
72966
72933
|
const now = performance.now();
|
|
@@ -72969,95 +72936,11 @@ class ChatComposerComponent {
|
|
|
72969
72936
|
this.typing.emit();
|
|
72970
72937
|
}
|
|
72971
72938
|
}
|
|
72972
|
-
// ─── Grabación de audio ───────────────────────────────────────────────────
|
|
72973
|
-
/** Selecciona un mimeType de audio soportado por el navegador. */
|
|
72974
|
-
pickAudioMime() {
|
|
72975
|
-
const candidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'];
|
|
72976
|
-
const MR = window.MediaRecorder;
|
|
72977
|
-
if (!MR?.isTypeSupported)
|
|
72978
|
-
return '';
|
|
72979
|
-
return candidates.find(c => MR.isTypeSupported(c)) ?? '';
|
|
72980
|
-
}
|
|
72981
|
-
async startRecording() {
|
|
72982
|
-
if (this.recording())
|
|
72983
|
-
return;
|
|
72984
|
-
if (!navigator.mediaDevices?.getUserMedia || !('MediaRecorder' in window)) {
|
|
72985
|
-
this.voice.emit(); // sin soporte: deja que el consumer decida (fallback)
|
|
72986
|
-
return;
|
|
72987
|
-
}
|
|
72988
|
-
try {
|
|
72989
|
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
72990
|
-
this.recStream = stream;
|
|
72991
|
-
const mimeType = this.pickAudioMime();
|
|
72992
|
-
this.recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
|
|
72993
|
-
this.recChunks = [];
|
|
72994
|
-
this.recorder.ondataavailable = e => {
|
|
72995
|
-
if (e.data.size > 0)
|
|
72996
|
-
this.recChunks.push(e.data);
|
|
72997
|
-
};
|
|
72998
|
-
this.recorder.start();
|
|
72999
|
-
this.recording.set(true);
|
|
73000
|
-
this.recSeconds.set(0);
|
|
73001
|
-
this.recTimer = setInterval(() => this.recSeconds.update(s => s + 1), 1000);
|
|
73002
|
-
}
|
|
73003
|
-
catch {
|
|
73004
|
-
this.cleanupRecording();
|
|
73005
|
-
}
|
|
73006
|
-
}
|
|
73007
|
-
/** Para la grabación y la stagea como adjunto pendiente (con preview de audio). */
|
|
73008
|
-
finishRecording() {
|
|
73009
|
-
const rec = this.recorder;
|
|
73010
|
-
if (!rec) {
|
|
73011
|
-
this.cleanupRecording();
|
|
73012
|
-
return;
|
|
73013
|
-
}
|
|
73014
|
-
rec.onstop = () => {
|
|
73015
|
-
const type = rec.mimeType || 'audio/webm';
|
|
73016
|
-
const blob = new Blob(this.recChunks, { type });
|
|
73017
|
-
const ext = type.includes('mp4') ? 'm4a' : 'webm';
|
|
73018
|
-
const file = new File([blob], `audio-${this.recSeconds()}s.${ext}`, { type });
|
|
73019
|
-
if (blob.size > 0)
|
|
73020
|
-
this.addPending(file);
|
|
73021
|
-
this.cleanupRecording();
|
|
73022
|
-
};
|
|
73023
|
-
rec.stop();
|
|
73024
|
-
}
|
|
73025
|
-
cancelRecording() {
|
|
73026
|
-
const rec = this.recorder;
|
|
73027
|
-
if (rec)
|
|
73028
|
-
rec.onstop = () => this.cleanupRecording();
|
|
73029
|
-
try {
|
|
73030
|
-
rec?.stop();
|
|
73031
|
-
}
|
|
73032
|
-
catch {
|
|
73033
|
-
this.cleanupRecording();
|
|
73034
|
-
}
|
|
73035
|
-
if (!rec)
|
|
73036
|
-
this.cleanupRecording();
|
|
73037
|
-
}
|
|
73038
|
-
cleanupRecording() {
|
|
73039
|
-
if (this.recTimer)
|
|
73040
|
-
clearInterval(this.recTimer);
|
|
73041
|
-
this.recTimer = undefined;
|
|
73042
|
-
this.recStream?.getTracks().forEach(t => t.stop());
|
|
73043
|
-
this.recStream = undefined;
|
|
73044
|
-
this.recorder = undefined;
|
|
73045
|
-
this.recChunks = [];
|
|
73046
|
-
this.recording.set(false);
|
|
73047
|
-
this.recSeconds.set(0);
|
|
73048
|
-
}
|
|
73049
|
-
ngOnDestroy() {
|
|
73050
|
-
this.cleanupRecording();
|
|
73051
|
-
for (const att of this.pending()) {
|
|
73052
|
-
if (att.url)
|
|
73053
|
-
URL.revokeObjectURL(att.url);
|
|
73054
|
-
}
|
|
73055
|
-
}
|
|
73056
72939
|
t(key) {
|
|
73057
72940
|
return this.i18n.t(key, 'ChatComposer');
|
|
73058
72941
|
}
|
|
73059
72942
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
73060
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatComposerComponent, isStandalone: true, selector: "val-chat-composer", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, replyingTo: { classPropertyName: "replyingTo", publicName: "replyingTo", isSignal: true, isRequired: false, transformFunction: null }, showAttach: { classPropertyName: "showAttach", publicName: "showAttach", isSignal: true, isRequired: false, transformFunction: null }, showMic: { classPropertyName: "showMic", publicName: "showMic", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", typing: "typing", voice: "voice", cancelReply: "cancelReply" }, ngImport: i0, template: `
|
|
72943
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatComposerComponent, isStandalone: true, selector: "val-chat-composer", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, replyingTo: { classPropertyName: "replyingTo", publicName: "replyingTo", isSignal: true, isRequired: false, transformFunction: null }, showAttach: { classPropertyName: "showAttach", publicName: "showAttach", isSignal: true, isRequired: false, transformFunction: null }, showEmoji: { classPropertyName: "showEmoji", publicName: "showEmoji", isSignal: true, isRequired: false, transformFunction: null }, showMic: { classPropertyName: "showMic", publicName: "showMic", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", typing: "typing", attach: "attach", voice: "voice", cancelReply: "cancelReply" }, ngImport: i0, template: `
|
|
73061
72944
|
<div class="composer">
|
|
73062
72945
|
@if (replyingTo()) {
|
|
73063
72946
|
<div class="reply-bar">
|
|
@@ -73071,84 +72954,52 @@ class ChatComposerComponent {
|
|
|
73071
72954
|
</div>
|
|
73072
72955
|
}
|
|
73073
72956
|
|
|
73074
|
-
@if (
|
|
73075
|
-
<div class="
|
|
73076
|
-
@for (
|
|
73077
|
-
|
|
73078
|
-
<div class="audio-chip">
|
|
73079
|
-
<audio controls preload="metadata" [src]="att.url"></audio>
|
|
73080
|
-
<button class="thumb-remove inline" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
|
|
73081
|
-
<ion-icon name="close-outline" aria-hidden="true" />
|
|
73082
|
-
</button>
|
|
73083
|
-
</div>
|
|
73084
|
-
} @else {
|
|
73085
|
-
<div class="thumb" [class.file]="att.kind === 'file'">
|
|
73086
|
-
@if (att.kind === 'image') {
|
|
73087
|
-
<img [src]="att.url" [alt]="att.file.name" />
|
|
73088
|
-
} @else {
|
|
73089
|
-
<ion-icon name="document-outline" aria-hidden="true" />
|
|
73090
|
-
<span class="thumb-name">{{ att.file.name }}</span>
|
|
73091
|
-
}
|
|
73092
|
-
<button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
|
|
73093
|
-
<ion-icon name="close-outline" aria-hidden="true" />
|
|
73094
|
-
</button>
|
|
73095
|
-
</div>
|
|
73096
|
-
}
|
|
72957
|
+
@if (emojiOpen()) {
|
|
72958
|
+
<div class="emoji-row">
|
|
72959
|
+
@for (e of quickEmojis; track e) {
|
|
72960
|
+
<button class="emoji" (click)="insertEmoji(e)">{{ e }}</button>
|
|
73097
72961
|
}
|
|
73098
72962
|
</div>
|
|
73099
72963
|
}
|
|
73100
72964
|
|
|
73101
|
-
|
|
73102
|
-
|
|
73103
|
-
<
|
|
73104
|
-
<ion-icon name="
|
|
73105
|
-
|
|
73106
|
-
|
|
73107
|
-
|
|
73108
|
-
<span class="rec-spacer"></span>
|
|
73109
|
-
<button class="send-btn" [attr.aria-label]="t('done')" (click)="finishRecording()">
|
|
73110
|
-
<ion-icon name="checkmark-outline" aria-hidden="true" />
|
|
73111
|
-
</button>
|
|
73112
|
-
</div>
|
|
73113
|
-
} @else {
|
|
73114
|
-
<div class="pill">
|
|
73115
|
-
@if (showAttach()) {
|
|
73116
|
-
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73117
|
-
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73118
|
-
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73119
|
-
</label>
|
|
73120
|
-
}
|
|
72965
|
+
<div class="pill">
|
|
72966
|
+
@if (showAttach()) {
|
|
72967
|
+
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
72968
|
+
<ion-icon name="add-outline" aria-hidden="true" />
|
|
72969
|
+
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
72970
|
+
</label>
|
|
72971
|
+
}
|
|
73121
72972
|
|
|
73122
|
-
|
|
73123
|
-
|
|
73124
|
-
|
|
73125
|
-
|
|
73126
|
-
|
|
73127
|
-
|
|
73128
|
-
|
|
73129
|
-
|
|
73130
|
-
|
|
73131
|
-
|
|
73132
|
-
|
|
73133
|
-
enterkeyhint="enter"
|
|
73134
|
-
(ionInput)="onInput($event)"
|
|
73135
|
-
(keydown)="onKeydown($event)"
|
|
73136
|
-
(paste)="onPaste($event)"
|
|
73137
|
-
></ion-textarea>
|
|
72973
|
+
<ion-textarea
|
|
72974
|
+
class="field"
|
|
72975
|
+
[placeholder]="placeholderText()"
|
|
72976
|
+
[disabled]="disabled()"
|
|
72977
|
+
[maxlength]="maxLength()"
|
|
72978
|
+
[autoGrow]="true"
|
|
72979
|
+
[rows]="1"
|
|
72980
|
+
[value]="body()"
|
|
72981
|
+
(ionInput)="onInput($event)"
|
|
72982
|
+
(keydown)="onKeydown($event)"
|
|
72983
|
+
></ion-textarea>
|
|
73138
72984
|
|
|
73139
|
-
|
|
73140
|
-
|
|
73141
|
-
|
|
73142
|
-
|
|
73143
|
-
|
|
73144
|
-
|
|
73145
|
-
|
|
73146
|
-
|
|
73147
|
-
|
|
73148
|
-
|
|
73149
|
-
|
|
72985
|
+
@if (showEmoji()) {
|
|
72986
|
+
<button class="icon-btn" [class.active]="emojiOpen()" [attr.aria-label]="t('emoji')" (click)="toggleEmoji()">
|
|
72987
|
+
<ion-icon name="happy-outline" aria-hidden="true" />
|
|
72988
|
+
</button>
|
|
72989
|
+
}
|
|
72990
|
+
|
|
72991
|
+
@if (canSend()) {
|
|
72992
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="onSend()">
|
|
72993
|
+
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
72994
|
+
</button>
|
|
72995
|
+
} @else if (showMic()) {
|
|
72996
|
+
<button class="icon-btn mic" [attr.aria-label]="t('voice')" (click)="voice.emit()">
|
|
72997
|
+
<ion-icon name="mic-outline" aria-hidden="true" />
|
|
72998
|
+
</button>
|
|
72999
|
+
}
|
|
73000
|
+
</div>
|
|
73150
73001
|
</div>
|
|
73151
|
-
`, isInline: true, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.
|
|
73002
|
+
`, isInline: true, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.emoji-row{display:flex;gap:4px;flex-wrap:wrap}.emoji{border:none;background:var(--ion-color-step-100, rgba(127, 127, 127, .1));border-radius:10px;padding:4px 8px;font-size:1.25rem;cursor:pointer}.pill{display:flex;align-items:flex-end;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.field{flex:1;--background: transparent;--padding-start: 6px;--padding-end: 6px;--padding-top: 8px;--padding-bottom: 8px;margin:0;max-height:120px;font-size:.9375rem}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.375rem}.icon-btn:hover,.icon-btn.active,.icon-btn.mic:hover{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff);cursor:pointer;font-size:1.25rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
|
|
73152
73003
|
}
|
|
73153
73004
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, decorators: [{
|
|
73154
73005
|
type: Component,
|
|
@@ -73166,84 +73017,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
73166
73017
|
</div>
|
|
73167
73018
|
}
|
|
73168
73019
|
|
|
73169
|
-
@if (
|
|
73170
|
-
<div class="
|
|
73171
|
-
@for (
|
|
73172
|
-
|
|
73173
|
-
<div class="audio-chip">
|
|
73174
|
-
<audio controls preload="metadata" [src]="att.url"></audio>
|
|
73175
|
-
<button class="thumb-remove inline" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
|
|
73176
|
-
<ion-icon name="close-outline" aria-hidden="true" />
|
|
73177
|
-
</button>
|
|
73178
|
-
</div>
|
|
73179
|
-
} @else {
|
|
73180
|
-
<div class="thumb" [class.file]="att.kind === 'file'">
|
|
73181
|
-
@if (att.kind === 'image') {
|
|
73182
|
-
<img [src]="att.url" [alt]="att.file.name" />
|
|
73183
|
-
} @else {
|
|
73184
|
-
<ion-icon name="document-outline" aria-hidden="true" />
|
|
73185
|
-
<span class="thumb-name">{{ att.file.name }}</span>
|
|
73186
|
-
}
|
|
73187
|
-
<button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
|
|
73188
|
-
<ion-icon name="close-outline" aria-hidden="true" />
|
|
73189
|
-
</button>
|
|
73190
|
-
</div>
|
|
73191
|
-
}
|
|
73020
|
+
@if (emojiOpen()) {
|
|
73021
|
+
<div class="emoji-row">
|
|
73022
|
+
@for (e of quickEmojis; track e) {
|
|
73023
|
+
<button class="emoji" (click)="insertEmoji(e)">{{ e }}</button>
|
|
73192
73024
|
}
|
|
73193
73025
|
</div>
|
|
73194
73026
|
}
|
|
73195
73027
|
|
|
73196
|
-
|
|
73197
|
-
|
|
73198
|
-
<
|
|
73199
|
-
<ion-icon name="
|
|
73200
|
-
|
|
73201
|
-
|
|
73202
|
-
|
|
73203
|
-
<span class="rec-spacer"></span>
|
|
73204
|
-
<button class="send-btn" [attr.aria-label]="t('done')" (click)="finishRecording()">
|
|
73205
|
-
<ion-icon name="checkmark-outline" aria-hidden="true" />
|
|
73206
|
-
</button>
|
|
73207
|
-
</div>
|
|
73208
|
-
} @else {
|
|
73209
|
-
<div class="pill">
|
|
73210
|
-
@if (showAttach()) {
|
|
73211
|
-
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73212
|
-
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73213
|
-
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73214
|
-
</label>
|
|
73215
|
-
}
|
|
73028
|
+
<div class="pill">
|
|
73029
|
+
@if (showAttach()) {
|
|
73030
|
+
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73031
|
+
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73032
|
+
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73033
|
+
</label>
|
|
73034
|
+
}
|
|
73216
73035
|
|
|
73217
|
-
|
|
73218
|
-
|
|
73219
|
-
|
|
73220
|
-
|
|
73221
|
-
|
|
73222
|
-
|
|
73223
|
-
|
|
73224
|
-
|
|
73225
|
-
|
|
73226
|
-
|
|
73227
|
-
|
|
73228
|
-
enterkeyhint="enter"
|
|
73229
|
-
(ionInput)="onInput($event)"
|
|
73230
|
-
(keydown)="onKeydown($event)"
|
|
73231
|
-
(paste)="onPaste($event)"
|
|
73232
|
-
></ion-textarea>
|
|
73036
|
+
<ion-textarea
|
|
73037
|
+
class="field"
|
|
73038
|
+
[placeholder]="placeholderText()"
|
|
73039
|
+
[disabled]="disabled()"
|
|
73040
|
+
[maxlength]="maxLength()"
|
|
73041
|
+
[autoGrow]="true"
|
|
73042
|
+
[rows]="1"
|
|
73043
|
+
[value]="body()"
|
|
73044
|
+
(ionInput)="onInput($event)"
|
|
73045
|
+
(keydown)="onKeydown($event)"
|
|
73046
|
+
></ion-textarea>
|
|
73233
73047
|
|
|
73234
|
-
|
|
73235
|
-
|
|
73236
|
-
|
|
73237
|
-
|
|
73238
|
-
|
|
73239
|
-
|
|
73240
|
-
|
|
73241
|
-
|
|
73242
|
-
|
|
73243
|
-
|
|
73244
|
-
|
|
73048
|
+
@if (showEmoji()) {
|
|
73049
|
+
<button class="icon-btn" [class.active]="emojiOpen()" [attr.aria-label]="t('emoji')" (click)="toggleEmoji()">
|
|
73050
|
+
<ion-icon name="happy-outline" aria-hidden="true" />
|
|
73051
|
+
</button>
|
|
73052
|
+
}
|
|
73053
|
+
|
|
73054
|
+
@if (canSend()) {
|
|
73055
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="onSend()">
|
|
73056
|
+
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
73057
|
+
</button>
|
|
73058
|
+
} @else if (showMic()) {
|
|
73059
|
+
<button class="icon-btn mic" [attr.aria-label]="t('voice')" (click)="voice.emit()">
|
|
73060
|
+
<ion-icon name="mic-outline" aria-hidden="true" />
|
|
73061
|
+
</button>
|
|
73062
|
+
}
|
|
73063
|
+
</div>
|
|
73245
73064
|
</div>
|
|
73246
|
-
`, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.
|
|
73065
|
+
`, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.emoji-row{display:flex;gap:4px;flex-wrap:wrap}.emoji{border:none;background:var(--ion-color-step-100, rgba(127, 127, 127, .1));border-radius:10px;padding:4px 8px;font-size:1.25rem;cursor:pointer}.pill{display:flex;align-items:flex-end;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.field{flex:1;--background: transparent;--padding-start: 6px;--padding-end: 6px;--padding-top: 8px;--padding-bottom: 8px;margin:0;max-height:120px;font-size:.9375rem}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.375rem}.icon-btn:hover,.icon-btn.active,.icon-btn.mic:hover{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff);cursor:pointer;font-size:1.25rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}\n"] }]
|
|
73247
73066
|
}], ctorParameters: () => [] });
|
|
73248
73067
|
|
|
73249
73068
|
/**
|
|
@@ -73463,6 +73282,7 @@ class ChatWindowComponent {
|
|
|
73463
73282
|
this.replyTo = output();
|
|
73464
73283
|
this.editMessage = output();
|
|
73465
73284
|
this.typing = output();
|
|
73285
|
+
this.attach = output();
|
|
73466
73286
|
this.voice = output();
|
|
73467
73287
|
this.msgsEl = viewChild('msgs');
|
|
73468
73288
|
this.replyingTo = signal(null);
|
|
@@ -73546,8 +73366,8 @@ class ChatWindowComponent {
|
|
|
73546
73366
|
this.atBottom.set(true);
|
|
73547
73367
|
this.hasNew.set(false);
|
|
73548
73368
|
}
|
|
73549
|
-
onSend(
|
|
73550
|
-
this.sendMessage.emit(
|
|
73369
|
+
onSend(body) {
|
|
73370
|
+
this.sendMessage.emit(body);
|
|
73551
73371
|
this.replyingTo.set(null);
|
|
73552
73372
|
}
|
|
73553
73373
|
onAction(event) {
|
|
@@ -73575,7 +73395,7 @@ class ChatWindowComponent {
|
|
|
73575
73395
|
return this.i18n.t(key, 'ChatWindow');
|
|
73576
73396
|
}
|
|
73577
73397
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
73578
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatWindowComponent, isStandalone: true, selector: "val-chat-window", inputs: { convId: { classPropertyName: "convId", publicName: "convId", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, currentUserId: { classPropertyName: "currentUserId", publicName: "currentUserId", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, typingUsers: { classPropertyName: "typingUsers", publicName: "typingUsers", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", loadMore: "loadMore", reactionClick: "reactionClick", deleteMessage: "deleteMessage", replyTo: "replyTo", editMessage: "editMessage", typing: "typing", voice: "voice" }, viewQueries: [{ propertyName: "msgsEl", first: true, predicate: ["msgs"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
73398
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatWindowComponent, isStandalone: true, selector: "val-chat-window", inputs: { convId: { classPropertyName: "convId", publicName: "convId", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, currentUserId: { classPropertyName: "currentUserId", publicName: "currentUserId", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, typingUsers: { classPropertyName: "typingUsers", publicName: "typingUsers", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", loadMore: "loadMore", reactionClick: "reactionClick", deleteMessage: "deleteMessage", replyTo: "replyTo", editMessage: "editMessage", typing: "typing", attach: "attach", voice: "voice" }, viewQueries: [{ propertyName: "msgsEl", first: true, predicate: ["msgs"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
73579
73399
|
<div class="chat">
|
|
73580
73400
|
<div #msgs class="messages" (scroll)="onScroll()">
|
|
73581
73401
|
@if (isLoading()) {
|
|
@@ -73622,6 +73442,7 @@ class ChatWindowComponent {
|
|
|
73622
73442
|
[replyingTo]="replyingTo()"
|
|
73623
73443
|
(send)="onSend($event)"
|
|
73624
73444
|
(typing)="typing.emit()"
|
|
73445
|
+
(attach)="attach.emit($event)"
|
|
73625
73446
|
(voice)="voice.emit()"
|
|
73626
73447
|
(cancelReply)="replyingTo.set(null)"
|
|
73627
73448
|
/>
|
|
@@ -73629,7 +73450,7 @@ class ChatWindowComponent {
|
|
|
73629
73450
|
<div class="closed-banner">{{ t('conversationClosed') }}</div>
|
|
73630
73451
|
}
|
|
73631
73452
|
</div>
|
|
73632
|
-
`, isInline: true, styles: [":host{display:block;height:100%}.chat{position:relative;display:flex;flex-direction:column;height:100%;min-height:0}.messages{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}.state{margin:auto;display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ion-color-medium, #92949c);font-size:.9375rem;text-align:center;padding:24px}.state.empty ion-icon{font-size:2.5rem;opacity:.5}.date-sep{position:sticky;top:4px;z-index:1;display:flex;justify-content:center;margin:8px 0}.date-sep span{font-size:.75rem;color:var(--ion-color-medium, #92949c);background:var(--ion-color-step-100, rgba(127, 127, 127, .14));padding:3px 12px;border-radius:999px;backdrop-filter:blur(4px)}.scroll-down{position:absolute;right:14px;bottom:84px;width:40px;height:40px;border-radius:50%;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium, #92949c);box-shadow:0 2px 10px #00000026;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:1.25rem;z-index:3}.scroll-down .dot{position:absolute;top:0;right:0;width:12px;height:12px;border-radius:50%;background:var(--ion-color-primary);border:2px solid var(--ion-card-background, #fff)}.closed-banner{text-align:center;padding:14px;color:var(--ion-color-medium, #92949c);font-size:.875rem;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: MessageBubbleComponent, selector: "val-message-bubble", inputs: ["msg", "showAvatar", "showName", "tail", "locale"], outputs: ["action"] }, { kind: "component", type: ChatComposerComponent, selector: "val-chat-composer", inputs: ["placeholder", "disabled", "maxLength", "replyingTo", "showAttach", "showMic"], outputs: ["send", "typing", "voice", "cancelReply"] }, { kind: "component", type: TypingIndicatorComponent, selector: "val-typing-indicator", inputs: ["typingUsers"] }] }); }
|
|
73453
|
+
`, isInline: true, styles: [":host{display:block;height:100%}.chat{position:relative;display:flex;flex-direction:column;height:100%;min-height:0}.messages{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}.state{margin:auto;display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ion-color-medium, #92949c);font-size:.9375rem;text-align:center;padding:24px}.state.empty ion-icon{font-size:2.5rem;opacity:.5}.date-sep{position:sticky;top:4px;z-index:1;display:flex;justify-content:center;margin:8px 0}.date-sep span{font-size:.75rem;color:var(--ion-color-medium, #92949c);background:var(--ion-color-step-100, rgba(127, 127, 127, .14));padding:3px 12px;border-radius:999px;backdrop-filter:blur(4px)}.scroll-down{position:absolute;right:14px;bottom:84px;width:40px;height:40px;border-radius:50%;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium, #92949c);box-shadow:0 2px 10px #00000026;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:1.25rem;z-index:3}.scroll-down .dot{position:absolute;top:0;right:0;width:12px;height:12px;border-radius:50%;background:var(--ion-color-primary);border:2px solid var(--ion-card-background, #fff)}.closed-banner{text-align:center;padding:14px;color:var(--ion-color-medium, #92949c);font-size:.875rem;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: MessageBubbleComponent, selector: "val-message-bubble", inputs: ["msg", "showAvatar", "showName", "tail", "locale"], outputs: ["action"] }, { kind: "component", type: ChatComposerComponent, selector: "val-chat-composer", inputs: ["placeholder", "disabled", "maxLength", "replyingTo", "showAttach", "showEmoji", "showMic"], outputs: ["send", "typing", "attach", "voice", "cancelReply"] }, { kind: "component", type: TypingIndicatorComponent, selector: "val-typing-indicator", inputs: ["typingUsers"] }] }); }
|
|
73633
73454
|
}
|
|
73634
73455
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatWindowComponent, decorators: [{
|
|
73635
73456
|
type: Component,
|
|
@@ -73680,6 +73501,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
73680
73501
|
[replyingTo]="replyingTo()"
|
|
73681
73502
|
(send)="onSend($event)"
|
|
73682
73503
|
(typing)="typing.emit()"
|
|
73504
|
+
(attach)="attach.emit($event)"
|
|
73683
73505
|
(voice)="voice.emit()"
|
|
73684
73506
|
(cancelReply)="replyingTo.set(null)"
|
|
73685
73507
|
/>
|
|
@@ -73793,13 +73615,11 @@ class ThreadPanelComponent {
|
|
|
73793
73615
|
this.conversationSvc.stopTyping(conv.convId, orgId, appId);
|
|
73794
73616
|
}
|
|
73795
73617
|
}
|
|
73796
|
-
onSend(
|
|
73618
|
+
onSend(body) {
|
|
73797
73619
|
const conv = this.conv();
|
|
73798
73620
|
if (!conv || !this.conversationSvc)
|
|
73799
73621
|
return;
|
|
73800
|
-
|
|
73801
|
-
if (event.text)
|
|
73802
|
-
this.conversationSvc.sendMessage(conv.convId, event.text).subscribe();
|
|
73622
|
+
this.conversationSvc.sendMessage(conv.convId, body).subscribe();
|
|
73803
73623
|
}
|
|
73804
73624
|
onReactionClick(event) {
|
|
73805
73625
|
// Reacciones se implementan a nivel de app consumer
|
|
@@ -73848,7 +73668,7 @@ class ThreadPanelComponent {
|
|
|
73848
73668
|
</div>
|
|
73849
73669
|
}
|
|
73850
73670
|
</div>
|
|
73851
|
-
`, isInline: true, styles: [".thread-panel{display:flex;flex-direction:column;height:100%;background:var(--ion-background-color, #f4f5f8)}.thread-header{padding:12px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff))}.thread-header .thread-title{font-size:1rem;font-weight:600;color:var(--ion-text-color, #000);margin:0}.thread-loading{display:flex;align-items:center;justify-content:center;flex:1;gap:10px;color:var(--ion-color-medium, #92949c);font-size:.875rem}.thread-chat{flex:1;overflow:hidden;display:flex;flex-direction:column}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: ChatWindowComponent, selector: "val-chat-window", inputs: ["convId", "messages", "currentUserId", "isOpen", "typingUsers", "isLoading"], outputs: ["sendMessage", "loadMore", "reactionClick", "deleteMessage", "replyTo", "editMessage", "typing", "voice"] }] }); }
|
|
73671
|
+
`, isInline: true, styles: [".thread-panel{display:flex;flex-direction:column;height:100%;background:var(--ion-background-color, #f4f5f8)}.thread-header{padding:12px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff))}.thread-header .thread-title{font-size:1rem;font-weight:600;color:var(--ion-text-color, #000);margin:0}.thread-loading{display:flex;align-items:center;justify-content:center;flex:1;gap:10px;color:var(--ion-color-medium, #92949c);font-size:.875rem}.thread-chat{flex:1;overflow:hidden;display:flex;flex-direction:column}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: ChatWindowComponent, selector: "val-chat-window", inputs: ["convId", "messages", "currentUserId", "isOpen", "typingUsers", "isLoading"], outputs: ["sendMessage", "loadMore", "reactionClick", "deleteMessage", "replyTo", "editMessage", "typing", "attach", "voice"] }] }); }
|
|
73852
73672
|
}
|
|
73853
73673
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThreadPanelComponent, decorators: [{
|
|
73854
73674
|
type: Component,
|