valtech-components 4.0.231 → 4.0.233
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 +276 -104
- package/esm2022/lib/components/molecules/message-bubble/message-bubble.component.mjs +19 -4
- package/esm2022/lib/components/organisms/chat-window/chat-window.component.mjs +2 -2
- package/esm2022/lib/services/chat/conversation.service.mjs +15 -4
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +308 -110
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/chat-composer/chat-composer.component.d.ts +36 -13
- package/lib/components/molecules/message-bubble/message-bubble.component.d.ts +1 -0
- package/lib/services/chat/conversation.service.d.ts +5 -3
- package/lib/version.d.ts +1 -1
- package/package.json +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.233';
|
|
60
60
|
|
|
61
61
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
62
62
|
let isRefreshing = false;
|
|
@@ -72209,10 +72209,21 @@ class ConversationService {
|
|
|
72209
72209
|
return this.http.delete(`${this.baseUrl}/conversations/${convId}/participants/${userId}`);
|
|
72210
72210
|
}
|
|
72211
72211
|
/**
|
|
72212
|
-
* Envia un mensaje a una conversacion.
|
|
72212
|
+
* Envia un mensaje a una conversacion, opcionalmente con adjuntos.
|
|
72213
|
+
* El backend espera `attachments: { url, type, name, size }[]` (key `type` = MIME),
|
|
72214
|
+
* mientras que el front usa `mimeType` — se mapea aqui.
|
|
72213
72215
|
*/
|
|
72214
|
-
sendMessage(convId, body) {
|
|
72215
|
-
|
|
72216
|
+
sendMessage(convId, body, attachments) {
|
|
72217
|
+
const payload = { body };
|
|
72218
|
+
if (attachments?.length) {
|
|
72219
|
+
payload['attachments'] = attachments.map(a => ({
|
|
72220
|
+
url: a.url,
|
|
72221
|
+
type: a.mimeType,
|
|
72222
|
+
name: a.name,
|
|
72223
|
+
size: a.size ?? 0,
|
|
72224
|
+
}));
|
|
72225
|
+
}
|
|
72226
|
+
return this.http.post(`${this.baseUrl}/conversations/${convId}/messages`, payload);
|
|
72216
72227
|
}
|
|
72217
72228
|
/**
|
|
72218
72229
|
* Elimina (marca como eliminado) un mensaje.
|
|
@@ -72439,7 +72450,8 @@ class MessageBubbleComponent {
|
|
|
72439
72450
|
return (parts[0][0] + (parts[1]?.[0] ?? '')).toUpperCase();
|
|
72440
72451
|
});
|
|
72441
72452
|
this.imageAttachments = computed(() => (this.msg().attachments ?? []).filter(a => a.mimeType?.startsWith('image/')));
|
|
72442
|
-
this.
|
|
72453
|
+
this.audioAttachments = computed(() => (this.msg().attachments ?? []).filter(a => a.mimeType?.startsWith('audio/')));
|
|
72454
|
+
this.fileAttachments = computed(() => (this.msg().attachments ?? []).filter(a => !a.mimeType?.startsWith('image/') && !a.mimeType?.startsWith('audio/')));
|
|
72443
72455
|
this.statusIcon = computed(() => {
|
|
72444
72456
|
switch (this.msg().status) {
|
|
72445
72457
|
case 'sending':
|
|
@@ -72510,6 +72522,13 @@ class MessageBubbleComponent {
|
|
|
72510
72522
|
}
|
|
72511
72523
|
</div>
|
|
72512
72524
|
}
|
|
72525
|
+
@if (audioAttachments().length > 0) {
|
|
72526
|
+
<div class="audios">
|
|
72527
|
+
@for (att of audioAttachments(); track att.url) {
|
|
72528
|
+
<audio class="att-audio" controls preload="metadata" [src]="att.url"></audio>
|
|
72529
|
+
}
|
|
72530
|
+
</div>
|
|
72531
|
+
}
|
|
72513
72532
|
@if (fileAttachments().length > 0) {
|
|
72514
72533
|
<div class="files">
|
|
72515
72534
|
@for (att of fileAttachments(); track att.url) {
|
|
@@ -72573,7 +72592,7 @@ class MessageBubbleComponent {
|
|
|
72573
72592
|
}
|
|
72574
72593
|
</div>
|
|
72575
72594
|
</div>
|
|
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"] }] }); }
|
|
72595
|
+
`, 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}.audios{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.att-audio{width:240px;max-width:100%;height:40px}.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"] }] }); }
|
|
72577
72596
|
}
|
|
72578
72597
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MessageBubbleComponent, decorators: [{
|
|
72579
72598
|
type: Component,
|
|
@@ -72618,6 +72637,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
72618
72637
|
}
|
|
72619
72638
|
</div>
|
|
72620
72639
|
}
|
|
72640
|
+
@if (audioAttachments().length > 0) {
|
|
72641
|
+
<div class="audios">
|
|
72642
|
+
@for (att of audioAttachments(); track att.url) {
|
|
72643
|
+
<audio class="att-audio" controls preload="metadata" [src]="att.url"></audio>
|
|
72644
|
+
}
|
|
72645
|
+
</div>
|
|
72646
|
+
}
|
|
72621
72647
|
@if (fileAttachments().length > 0) {
|
|
72622
72648
|
<div class="files">
|
|
72623
72649
|
@for (att of fileAttachments(); track att.url) {
|
|
@@ -72681,7 +72707,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
72681
72707
|
}
|
|
72682
72708
|
</div>
|
|
72683
72709
|
</div>
|
|
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"] }]
|
|
72710
|
+
`, 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}.audios{display:flex;flex-direction:column;gap:4px;margin-bottom:4px}.att-audio{width:240px;max-width:100%;height:40px}.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"] }]
|
|
72685
72711
|
}], ctorParameters: () => [] });
|
|
72686
72712
|
|
|
72687
72713
|
addIcons({ sendOutline });
|
|
@@ -72834,35 +72860,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
72834
72860
|
type: Output
|
|
72835
72861
|
}] } });
|
|
72836
72862
|
|
|
72837
|
-
addIcons({ addOutline, closeOutline,
|
|
72863
|
+
addIcons({ addOutline, closeOutline, documentOutline, micOutline, arrowUpOutline, trashOutline });
|
|
72838
72864
|
const CHAT_COMPOSER_I18N = {
|
|
72839
72865
|
es: {
|
|
72840
72866
|
placeholder: 'Escribe un mensaje',
|
|
72841
72867
|
send: 'Enviar',
|
|
72842
72868
|
attach: 'Adjuntar',
|
|
72843
|
-
emoji: 'Emoji',
|
|
72844
72869
|
voice: 'Mensaje de voz',
|
|
72845
72870
|
replyingTo: 'Respondiendo a',
|
|
72846
72871
|
cancel: 'Cancelar',
|
|
72872
|
+
remove: 'Quitar',
|
|
72847
72873
|
},
|
|
72848
72874
|
en: {
|
|
72849
72875
|
placeholder: 'Write a message',
|
|
72850
72876
|
send: 'Send',
|
|
72851
72877
|
attach: 'Attach',
|
|
72852
|
-
emoji: 'Emoji',
|
|
72853
72878
|
voice: 'Voice message',
|
|
72854
72879
|
replyingTo: 'Replying to',
|
|
72855
72880
|
cancel: 'Cancel',
|
|
72881
|
+
remove: 'Remove',
|
|
72856
72882
|
},
|
|
72857
72883
|
};
|
|
72858
|
-
const QUICK_EMOJIS = ['👍', '❤️', '😂', '🎉', '🙏', '🔥', '😮', '😢'];
|
|
72859
72884
|
/**
|
|
72860
72885
|
* val-chat-composer — barra de redacción de chat moderna (signal inputs).
|
|
72861
72886
|
*
|
|
72862
|
-
* Pill con: adjuntar (+), textarea auto-grow,
|
|
72863
|
-
*
|
|
72864
|
-
*
|
|
72865
|
-
*
|
|
72887
|
+
* Pill con: adjuntar (+), textarea auto-grow, y mic/enviar (mic cuando está vacío,
|
|
72888
|
+
* enviar cuando hay texto o adjuntos). Los adjuntos (botón + / pegar imagen) se
|
|
72889
|
+
* acumulan como PREVIEW (miniaturas) y se emiten por `(attach)` recién al ENVIAR
|
|
72890
|
+
* — no se van solos al chat. Audio grabado se emite directo al parar.
|
|
72891
|
+
* No incluye picker de emoji: el teclado del sistema ya lo trae.
|
|
72866
72892
|
*/
|
|
72867
72893
|
class ChatComposerComponent {
|
|
72868
72894
|
constructor() {
|
|
@@ -72872,7 +72898,6 @@ class ChatComposerComponent {
|
|
|
72872
72898
|
this.maxLength = input(2000);
|
|
72873
72899
|
this.replyingTo = input(null);
|
|
72874
72900
|
this.showAttach = input(true);
|
|
72875
|
-
this.showEmoji = input(true);
|
|
72876
72901
|
this.showMic = input(true);
|
|
72877
72902
|
this.send = output();
|
|
72878
72903
|
this.typing = output();
|
|
@@ -72880,12 +72905,24 @@ class ChatComposerComponent {
|
|
|
72880
72905
|
this.voice = output();
|
|
72881
72906
|
this.cancelReply = output();
|
|
72882
72907
|
this.body = signal('');
|
|
72883
|
-
this.
|
|
72884
|
-
this.
|
|
72908
|
+
this.pending = signal([]);
|
|
72909
|
+
this.pendingId = 0;
|
|
72910
|
+
// Grabación de audio (MediaRecorder).
|
|
72911
|
+
this.recording = signal(false);
|
|
72912
|
+
this.recSeconds = signal(0);
|
|
72913
|
+
this.recChunks = [];
|
|
72914
|
+
this.recTimeLabel = computed(() => {
|
|
72915
|
+
const s = this.recSeconds();
|
|
72916
|
+
const mm = String(Math.floor(s / 60)).padStart(2, '0');
|
|
72917
|
+
const ss = String(s % 60).padStart(2, '0');
|
|
72918
|
+
return `${mm}:${ss}`;
|
|
72919
|
+
});
|
|
72885
72920
|
this.lastTypingEmit = 0;
|
|
72886
72921
|
this.canSend = computed(() => {
|
|
72887
72922
|
const t = this.body().trim();
|
|
72888
|
-
|
|
72923
|
+
if (this.disabled())
|
|
72924
|
+
return false;
|
|
72925
|
+
return (t.length > 0 && t.length <= this.maxLength()) || this.pending().length > 0;
|
|
72889
72926
|
});
|
|
72890
72927
|
this.placeholderText = computed(() => {
|
|
72891
72928
|
this.i18n.lang();
|
|
@@ -72905,29 +72942,61 @@ class ChatComposerComponent {
|
|
|
72905
72942
|
}
|
|
72906
72943
|
}
|
|
72907
72944
|
onSend() {
|
|
72908
|
-
|
|
72909
|
-
if (!trimmed || trimmed.length > this.maxLength())
|
|
72945
|
+
if (!this.canSend())
|
|
72910
72946
|
return;
|
|
72911
|
-
|
|
72947
|
+
// Primero los adjuntos pendientes (cada uno se sube y manda en el consumer),
|
|
72948
|
+
// luego el texto como mensaje aparte si lo hay.
|
|
72949
|
+
const files = this.pending();
|
|
72950
|
+
for (const att of files) {
|
|
72951
|
+
this.attach.emit(att.file);
|
|
72952
|
+
if (att.url)
|
|
72953
|
+
URL.revokeObjectURL(att.url);
|
|
72954
|
+
}
|
|
72955
|
+
this.pending.set([]);
|
|
72956
|
+
const trimmed = this.body().trim();
|
|
72957
|
+
if (trimmed && trimmed.length <= this.maxLength()) {
|
|
72958
|
+
this.send.emit(trimmed);
|
|
72959
|
+
}
|
|
72912
72960
|
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);
|
|
72920
72961
|
}
|
|
72921
72962
|
onFile(event) {
|
|
72922
72963
|
const input = event.target;
|
|
72923
72964
|
const file = input.files?.[0];
|
|
72924
72965
|
if (file)
|
|
72925
|
-
this.
|
|
72966
|
+
this.addPending(file);
|
|
72926
72967
|
input.value = '';
|
|
72927
72968
|
}
|
|
72969
|
+
/** Pegar imagen desde el portapapeles (captura de pantalla, copiar imagen). */
|
|
72970
|
+
onPaste(event) {
|
|
72971
|
+
const items = event.clipboardData?.items;
|
|
72972
|
+
if (!items)
|
|
72973
|
+
return;
|
|
72974
|
+
for (const item of Array.from(items)) {
|
|
72975
|
+
if (item.type.startsWith('image/')) {
|
|
72976
|
+
const file = item.getAsFile();
|
|
72977
|
+
if (file) {
|
|
72978
|
+
event.preventDefault();
|
|
72979
|
+
this.addPending(file);
|
|
72980
|
+
}
|
|
72981
|
+
return;
|
|
72982
|
+
}
|
|
72983
|
+
}
|
|
72984
|
+
}
|
|
72985
|
+
addPending(file) {
|
|
72986
|
+
const isImage = file.type.startsWith('image/');
|
|
72987
|
+
const url = isImage ? URL.createObjectURL(file) : undefined;
|
|
72988
|
+
this.pending.update(arr => [...arr, { id: this.pendingId++, file, url, isImage }]);
|
|
72989
|
+
}
|
|
72990
|
+
removePending(id) {
|
|
72991
|
+
this.pending.update(arr => {
|
|
72992
|
+
const found = arr.find(a => a.id === id);
|
|
72993
|
+
if (found?.url)
|
|
72994
|
+
URL.revokeObjectURL(found.url);
|
|
72995
|
+
return arr.filter(a => a.id !== id);
|
|
72996
|
+
});
|
|
72997
|
+
}
|
|
72928
72998
|
/**
|
|
72929
|
-
* Emite typing como máximo 1 vez cada 2s.
|
|
72930
|
-
* de workflow; aquí estamos en runtime de navegador, performance.now es seguro.
|
|
72999
|
+
* Emite typing como máximo 1 vez cada 2s. performance.now es seguro en runtime.
|
|
72931
73000
|
*/
|
|
72932
73001
|
emitTypingDebounced() {
|
|
72933
73002
|
const now = performance.now();
|
|
@@ -72936,11 +73005,94 @@ class ChatComposerComponent {
|
|
|
72936
73005
|
this.typing.emit();
|
|
72937
73006
|
}
|
|
72938
73007
|
}
|
|
73008
|
+
// ─── Grabación de audio ───────────────────────────────────────────────────
|
|
73009
|
+
/** Selecciona un mimeType de audio soportado por el navegador. */
|
|
73010
|
+
pickAudioMime() {
|
|
73011
|
+
const candidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'];
|
|
73012
|
+
const MR = window.MediaRecorder;
|
|
73013
|
+
if (!MR?.isTypeSupported)
|
|
73014
|
+
return '';
|
|
73015
|
+
return candidates.find(c => MR.isTypeSupported(c)) ?? '';
|
|
73016
|
+
}
|
|
73017
|
+
async startRecording() {
|
|
73018
|
+
if (this.recording())
|
|
73019
|
+
return;
|
|
73020
|
+
if (!navigator.mediaDevices?.getUserMedia || !('MediaRecorder' in window)) {
|
|
73021
|
+
this.voice.emit(); // sin soporte: deja que el consumer decida (fallback)
|
|
73022
|
+
return;
|
|
73023
|
+
}
|
|
73024
|
+
try {
|
|
73025
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
73026
|
+
this.recStream = stream;
|
|
73027
|
+
const mimeType = this.pickAudioMime();
|
|
73028
|
+
this.recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
|
|
73029
|
+
this.recChunks = [];
|
|
73030
|
+
this.recorder.ondataavailable = e => {
|
|
73031
|
+
if (e.data.size > 0)
|
|
73032
|
+
this.recChunks.push(e.data);
|
|
73033
|
+
};
|
|
73034
|
+
this.recorder.start();
|
|
73035
|
+
this.recording.set(true);
|
|
73036
|
+
this.recSeconds.set(0);
|
|
73037
|
+
this.recTimer = setInterval(() => this.recSeconds.update(s => s + 1), 1000);
|
|
73038
|
+
}
|
|
73039
|
+
catch {
|
|
73040
|
+
this.cleanupRecording();
|
|
73041
|
+
}
|
|
73042
|
+
}
|
|
73043
|
+
stopAndSend() {
|
|
73044
|
+
const rec = this.recorder;
|
|
73045
|
+
if (!rec) {
|
|
73046
|
+
this.cleanupRecording();
|
|
73047
|
+
return;
|
|
73048
|
+
}
|
|
73049
|
+
rec.onstop = () => {
|
|
73050
|
+
const type = rec.mimeType || 'audio/webm';
|
|
73051
|
+
const blob = new Blob(this.recChunks, { type });
|
|
73052
|
+
const ext = type.includes('mp4') ? 'm4a' : 'webm';
|
|
73053
|
+
const file = new File([blob], `audio-${this.recSeconds()}s.${ext}`, { type });
|
|
73054
|
+
if (blob.size > 0)
|
|
73055
|
+
this.attach.emit(file);
|
|
73056
|
+
this.cleanupRecording();
|
|
73057
|
+
};
|
|
73058
|
+
rec.stop();
|
|
73059
|
+
}
|
|
73060
|
+
cancelRecording() {
|
|
73061
|
+
const rec = this.recorder;
|
|
73062
|
+
if (rec)
|
|
73063
|
+
rec.onstop = () => this.cleanupRecording();
|
|
73064
|
+
try {
|
|
73065
|
+
rec?.stop();
|
|
73066
|
+
}
|
|
73067
|
+
catch {
|
|
73068
|
+
this.cleanupRecording();
|
|
73069
|
+
}
|
|
73070
|
+
if (!rec)
|
|
73071
|
+
this.cleanupRecording();
|
|
73072
|
+
}
|
|
73073
|
+
cleanupRecording() {
|
|
73074
|
+
if (this.recTimer)
|
|
73075
|
+
clearInterval(this.recTimer);
|
|
73076
|
+
this.recTimer = undefined;
|
|
73077
|
+
this.recStream?.getTracks().forEach(t => t.stop());
|
|
73078
|
+
this.recStream = undefined;
|
|
73079
|
+
this.recorder = undefined;
|
|
73080
|
+
this.recChunks = [];
|
|
73081
|
+
this.recording.set(false);
|
|
73082
|
+
this.recSeconds.set(0);
|
|
73083
|
+
}
|
|
73084
|
+
ngOnDestroy() {
|
|
73085
|
+
this.cleanupRecording();
|
|
73086
|
+
for (const att of this.pending()) {
|
|
73087
|
+
if (att.url)
|
|
73088
|
+
URL.revokeObjectURL(att.url);
|
|
73089
|
+
}
|
|
73090
|
+
}
|
|
72939
73091
|
t(key) {
|
|
72940
73092
|
return this.i18n.t(key, 'ChatComposer');
|
|
72941
73093
|
}
|
|
72942
73094
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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 },
|
|
73095
|
+
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", attach: "attach", voice: "voice", cancelReply: "cancelReply" }, ngImport: i0, template: `
|
|
72944
73096
|
<div class="composer">
|
|
72945
73097
|
@if (replyingTo()) {
|
|
72946
73098
|
<div class="reply-bar">
|
|
@@ -72954,52 +73106,75 @@ class ChatComposerComponent {
|
|
|
72954
73106
|
</div>
|
|
72955
73107
|
}
|
|
72956
73108
|
|
|
72957
|
-
@if (
|
|
72958
|
-
<div class="
|
|
72959
|
-
@for (
|
|
72960
|
-
<
|
|
73109
|
+
@if (pending().length > 0) {
|
|
73110
|
+
<div class="pending-row">
|
|
73111
|
+
@for (att of pending(); track att.id) {
|
|
73112
|
+
<div class="thumb" [class.file]="!att.isImage">
|
|
73113
|
+
@if (att.isImage) {
|
|
73114
|
+
<img [src]="att.url" [alt]="att.file.name" />
|
|
73115
|
+
} @else {
|
|
73116
|
+
<ion-icon name="document-outline" aria-hidden="true" />
|
|
73117
|
+
<span class="thumb-name">{{ att.file.name }}</span>
|
|
73118
|
+
}
|
|
73119
|
+
<button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
|
|
73120
|
+
<ion-icon name="close-outline" aria-hidden="true" />
|
|
73121
|
+
</button>
|
|
73122
|
+
</div>
|
|
72961
73123
|
}
|
|
72962
73124
|
</div>
|
|
72963
73125
|
}
|
|
72964
73126
|
|
|
72965
|
-
|
|
72966
|
-
|
|
72967
|
-
<
|
|
72968
|
-
<ion-icon name="
|
|
72969
|
-
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
72970
|
-
</label>
|
|
72971
|
-
}
|
|
72972
|
-
|
|
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>
|
|
72984
|
-
|
|
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" />
|
|
73127
|
+
@if (recording()) {
|
|
73128
|
+
<div class="rec-bar">
|
|
73129
|
+
<button class="icon-btn danger" [attr.aria-label]="t('cancel')" (click)="cancelRecording()">
|
|
73130
|
+
<ion-icon name="trash-outline" aria-hidden="true" />
|
|
72988
73131
|
</button>
|
|
72989
|
-
|
|
72990
|
-
|
|
72991
|
-
|
|
72992
|
-
<button class="send-btn" [attr.aria-label]="t('send')" (click)="
|
|
73132
|
+
<span class="rec-dot"></span>
|
|
73133
|
+
<span class="rec-time">{{ recTimeLabel() }}</span>
|
|
73134
|
+
<span class="rec-spacer"></span>
|
|
73135
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="stopAndSend()">
|
|
72993
73136
|
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
72994
73137
|
</button>
|
|
72995
|
-
|
|
72996
|
-
|
|
72997
|
-
|
|
72998
|
-
|
|
72999
|
-
|
|
73000
|
-
|
|
73138
|
+
</div>
|
|
73139
|
+
} @else {
|
|
73140
|
+
<div class="pill">
|
|
73141
|
+
@if (showAttach()) {
|
|
73142
|
+
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73143
|
+
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73144
|
+
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73145
|
+
</label>
|
|
73146
|
+
}
|
|
73147
|
+
|
|
73148
|
+
<ion-textarea
|
|
73149
|
+
class="field"
|
|
73150
|
+
[placeholder]="placeholderText()"
|
|
73151
|
+
[disabled]="disabled()"
|
|
73152
|
+
[maxlength]="maxLength()"
|
|
73153
|
+
[autoGrow]="true"
|
|
73154
|
+
[rows]="1"
|
|
73155
|
+
[value]="body()"
|
|
73156
|
+
[spellcheck]="false"
|
|
73157
|
+
autocapitalize="sentences"
|
|
73158
|
+
autocorrect="off"
|
|
73159
|
+
enterkeyhint="enter"
|
|
73160
|
+
(ionInput)="onInput($event)"
|
|
73161
|
+
(keydown)="onKeydown($event)"
|
|
73162
|
+
(paste)="onPaste($event)"
|
|
73163
|
+
></ion-textarea>
|
|
73164
|
+
|
|
73165
|
+
@if (canSend()) {
|
|
73166
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="onSend()">
|
|
73167
|
+
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
73168
|
+
</button>
|
|
73169
|
+
} @else if (showMic()) {
|
|
73170
|
+
<button class="icon-btn mic" [attr.aria-label]="t('voice')" (click)="startRecording()">
|
|
73171
|
+
<ion-icon name="mic-outline" aria-hidden="true" />
|
|
73172
|
+
</button>
|
|
73173
|
+
}
|
|
73174
|
+
</div>
|
|
73175
|
+
}
|
|
73001
73176
|
</div>
|
|
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)}.
|
|
73177
|
+
`, 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)}.pending-row{display:flex;gap:8px;flex-wrap:wrap;padding:2px 4px}.thumb{position:relative;width:64px;height:64px;border-radius:10px;overflow:hidden;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.thumb img{width:100%;height:100%;object-fit:cover}.thumb.file{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;color:var(--ion-color-medium, #92949c);font-size:1.5rem}.thumb-name{font-size:.5625rem;line-height:1.1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ion-text-color, #000)}.thumb-remove{position:absolute;top:2px;right:2px;width:20px;height:20px;border:none;border-radius:50%;background:#0000008c;color:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:.875rem}.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{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)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.rec-bar{display:flex;align-items:center;gap:10px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--ion-color-danger, #eb445a);animation:rec-pulse 1.2s ease-in-out infinite}.rec-time{font-variant-numeric:tabular-nums;font-size:.9375rem;color:var(--ion-text-color, #000)}.rec-spacer{flex:1}@keyframes rec-pulse{0%,to{opacity:1}50%{opacity:.3}}\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"] }] }); }
|
|
73003
73178
|
}
|
|
73004
73179
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, decorators: [{
|
|
73005
73180
|
type: Component,
|
|
@@ -73017,52 +73192,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
73017
73192
|
</div>
|
|
73018
73193
|
}
|
|
73019
73194
|
|
|
73020
|
-
@if (
|
|
73021
|
-
<div class="
|
|
73022
|
-
@for (
|
|
73023
|
-
<
|
|
73195
|
+
@if (pending().length > 0) {
|
|
73196
|
+
<div class="pending-row">
|
|
73197
|
+
@for (att of pending(); track att.id) {
|
|
73198
|
+
<div class="thumb" [class.file]="!att.isImage">
|
|
73199
|
+
@if (att.isImage) {
|
|
73200
|
+
<img [src]="att.url" [alt]="att.file.name" />
|
|
73201
|
+
} @else {
|
|
73202
|
+
<ion-icon name="document-outline" aria-hidden="true" />
|
|
73203
|
+
<span class="thumb-name">{{ att.file.name }}</span>
|
|
73204
|
+
}
|
|
73205
|
+
<button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
|
|
73206
|
+
<ion-icon name="close-outline" aria-hidden="true" />
|
|
73207
|
+
</button>
|
|
73208
|
+
</div>
|
|
73024
73209
|
}
|
|
73025
73210
|
</div>
|
|
73026
73211
|
}
|
|
73027
73212
|
|
|
73028
|
-
|
|
73029
|
-
|
|
73030
|
-
<
|
|
73031
|
-
<ion-icon name="
|
|
73032
|
-
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73033
|
-
</label>
|
|
73034
|
-
}
|
|
73035
|
-
|
|
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>
|
|
73047
|
-
|
|
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" />
|
|
73213
|
+
@if (recording()) {
|
|
73214
|
+
<div class="rec-bar">
|
|
73215
|
+
<button class="icon-btn danger" [attr.aria-label]="t('cancel')" (click)="cancelRecording()">
|
|
73216
|
+
<ion-icon name="trash-outline" aria-hidden="true" />
|
|
73051
73217
|
</button>
|
|
73052
|
-
|
|
73053
|
-
|
|
73054
|
-
|
|
73055
|
-
<button class="send-btn" [attr.aria-label]="t('send')" (click)="
|
|
73218
|
+
<span class="rec-dot"></span>
|
|
73219
|
+
<span class="rec-time">{{ recTimeLabel() }}</span>
|
|
73220
|
+
<span class="rec-spacer"></span>
|
|
73221
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="stopAndSend()">
|
|
73056
73222
|
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
73057
73223
|
</button>
|
|
73058
|
-
|
|
73059
|
-
|
|
73060
|
-
|
|
73061
|
-
|
|
73062
|
-
|
|
73063
|
-
|
|
73224
|
+
</div>
|
|
73225
|
+
} @else {
|
|
73226
|
+
<div class="pill">
|
|
73227
|
+
@if (showAttach()) {
|
|
73228
|
+
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73229
|
+
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73230
|
+
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73231
|
+
</label>
|
|
73232
|
+
}
|
|
73233
|
+
|
|
73234
|
+
<ion-textarea
|
|
73235
|
+
class="field"
|
|
73236
|
+
[placeholder]="placeholderText()"
|
|
73237
|
+
[disabled]="disabled()"
|
|
73238
|
+
[maxlength]="maxLength()"
|
|
73239
|
+
[autoGrow]="true"
|
|
73240
|
+
[rows]="1"
|
|
73241
|
+
[value]="body()"
|
|
73242
|
+
[spellcheck]="false"
|
|
73243
|
+
autocapitalize="sentences"
|
|
73244
|
+
autocorrect="off"
|
|
73245
|
+
enterkeyhint="enter"
|
|
73246
|
+
(ionInput)="onInput($event)"
|
|
73247
|
+
(keydown)="onKeydown($event)"
|
|
73248
|
+
(paste)="onPaste($event)"
|
|
73249
|
+
></ion-textarea>
|
|
73250
|
+
|
|
73251
|
+
@if (canSend()) {
|
|
73252
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="onSend()">
|
|
73253
|
+
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
73254
|
+
</button>
|
|
73255
|
+
} @else if (showMic()) {
|
|
73256
|
+
<button class="icon-btn mic" [attr.aria-label]="t('voice')" (click)="startRecording()">
|
|
73257
|
+
<ion-icon name="mic-outline" aria-hidden="true" />
|
|
73258
|
+
</button>
|
|
73259
|
+
}
|
|
73260
|
+
</div>
|
|
73261
|
+
}
|
|
73064
73262
|
</div>
|
|
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)}.
|
|
73263
|
+
`, 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)}.pending-row{display:flex;gap:8px;flex-wrap:wrap;padding:2px 4px}.thumb{position:relative;width:64px;height:64px;border-radius:10px;overflow:hidden;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.thumb img{width:100%;height:100%;object-fit:cover}.thumb.file{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;color:var(--ion-color-medium, #92949c);font-size:1.5rem}.thumb-name{font-size:.5625rem;line-height:1.1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ion-text-color, #000)}.thumb-remove{position:absolute;top:2px;right:2px;width:20px;height:20px;border:none;border-radius:50%;background:#0000008c;color:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:.875rem}.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{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)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.rec-bar{display:flex;align-items:center;gap:10px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--ion-color-danger, #eb445a);animation:rec-pulse 1.2s ease-in-out infinite}.rec-time{font-variant-numeric:tabular-nums;font-size:.9375rem;color:var(--ion-text-color, #000)}.rec-spacer{flex:1}@keyframes rec-pulse{0%,to{opacity:1}50%{opacity:.3}}\n"] }]
|
|
73066
73264
|
}], ctorParameters: () => [] });
|
|
73067
73265
|
|
|
73068
73266
|
/**
|
|
@@ -73450,7 +73648,7 @@ class ChatWindowComponent {
|
|
|
73450
73648
|
<div class="closed-banner">{{ t('conversationClosed') }}</div>
|
|
73451
73649
|
}
|
|
73452
73650
|
</div>
|
|
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", "
|
|
73651
|
+
`, 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", "attach", "voice", "cancelReply"] }, { kind: "component", type: TypingIndicatorComponent, selector: "val-typing-indicator", inputs: ["typingUsers"] }] }); }
|
|
73454
73652
|
}
|
|
73455
73653
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatWindowComponent, decorators: [{
|
|
73456
73654
|
type: Component,
|