@acorex/components 22.0.0-next.25 → 22.0.0-next.27
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/combo-box/README.md +26 -0
- package/fesm2022/acorex-components-combo-box.mjs +237 -0
- package/fesm2022/acorex-components-combo-box.mjs.map +1 -0
- package/fesm2022/acorex-components-conversation.mjs +937 -670
- package/fesm2022/acorex-components-conversation.mjs.map +1 -1
- package/fesm2022/acorex-components-data-table.mjs +42 -36
- package/fesm2022/acorex-components-data-table.mjs.map +1 -1
- package/fesm2022/acorex-components-dialog.mjs +3 -2
- package/fesm2022/acorex-components-dialog.mjs.map +1 -1
- package/package.json +7 -3
- package/types/acorex-components-combo-box.d.ts +114 -0
- package/types/acorex-components-conversation.d.ts +126 -32
|
@@ -3,7 +3,7 @@ import { AXTabsComponent, AXTabItemComponent } from '@acorex/components/tabs';
|
|
|
3
3
|
import * as i2 from '@angular/common';
|
|
4
4
|
import { CommonModule, NgComponentOutlet, isPlatformBrowser, DOCUMENT, AsyncPipe } from '@angular/common';
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
6
|
-
import { InjectionToken,
|
|
6
|
+
import { InjectionToken, inject, Injectable, signal, computed, Injector, runInInjectionContext, PLATFORM_ID, DestroyRef, viewChild, ViewContainerRef, input, output, effect, ViewEncapsulation, Component, ElementRef, HostListener, Directive, EventEmitter, afterNextRender, untracked, model, viewChildren, SecurityContext, NgModule } from '@angular/core';
|
|
7
7
|
import { AXUploaderService, AXUploaderZoneDirective, AXUploaderBrowseDirective } from '@acorex/cdk/uploader';
|
|
8
8
|
import { AXDialogService } from '@acorex/components/dialog';
|
|
9
9
|
import { AXPopupService } from '@acorex/components/popup';
|
|
@@ -31,6 +31,7 @@ import { AXListComponent } from '@acorex/components/list';
|
|
|
31
31
|
import * as i1$4 from '@acorex/components/search-box';
|
|
32
32
|
import { AXSearchBoxComponent, AXSearchBoxModule } from '@acorex/components/search-box';
|
|
33
33
|
import { AXBadgeComponent } from '@acorex/components/badge';
|
|
34
|
+
import { AXFabComponent } from '@acorex/components/fab';
|
|
34
35
|
import { AXContextMenuComponent } from '@acorex/components/menu';
|
|
35
36
|
import { AXDateTimeModule } from '@acorex/core/date-time';
|
|
36
37
|
import * as i3$1 from '@acorex/core/format';
|
|
@@ -54,319 +55,6 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|
|
54
55
|
* Common types used across all API services
|
|
55
56
|
*/
|
|
56
57
|
|
|
57
|
-
/**
|
|
58
|
-
* Abstract Conversation Management API
|
|
59
|
-
* Handle conversation CRUD operations, participants, and settings
|
|
60
|
-
*/
|
|
61
|
-
/**
|
|
62
|
-
* Abstract Conversation Management API
|
|
63
|
-
*
|
|
64
|
-
* Implement this abstract class to handle conversation operations.
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```typescript
|
|
68
|
-
* @Injectable()
|
|
69
|
-
* export class MyConversationApi extends AXConversationManagementApi {
|
|
70
|
-
* constructor(private http: HttpClient) {
|
|
71
|
-
* super();
|
|
72
|
-
* }
|
|
73
|
-
*
|
|
74
|
-
* async createConversation(data: AXConversationCreateData): Promise<AXConversation> {
|
|
75
|
-
* const response = await this.http.post('/api/conversations', data).toPromise();
|
|
76
|
-
* return response;
|
|
77
|
-
* }
|
|
78
|
-
*
|
|
79
|
-
* // ... implement other methods
|
|
80
|
-
* }
|
|
81
|
-
* ```
|
|
82
|
-
*/
|
|
83
|
-
class AXConversationApi {
|
|
84
|
-
// =====================
|
|
85
|
-
// Error Handling Helper
|
|
86
|
-
// =====================
|
|
87
|
-
/**
|
|
88
|
-
* Create an API error
|
|
89
|
-
* Helper method for consistent error creation
|
|
90
|
-
*
|
|
91
|
-
* @param code - Error code
|
|
92
|
-
* @param message - Error message
|
|
93
|
-
* @param statusCode - HTTP status code
|
|
94
|
-
* @param details - Additional details
|
|
95
|
-
* @returns API error object
|
|
96
|
-
*/
|
|
97
|
-
createError(code, message, statusCode, details) {
|
|
98
|
-
return {
|
|
99
|
-
code,
|
|
100
|
-
message,
|
|
101
|
-
statusCode,
|
|
102
|
-
details,
|
|
103
|
-
timestamp: new Date(),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Abstract Message Management API
|
|
110
|
-
* Handle message CRUD operations, reactions, and interactions
|
|
111
|
-
*/
|
|
112
|
-
/**
|
|
113
|
-
* Abstract Message Management API
|
|
114
|
-
*
|
|
115
|
-
* Implement this abstract class to handle message operations.
|
|
116
|
-
*
|
|
117
|
-
* @example
|
|
118
|
-
* ```typescript
|
|
119
|
-
* @Injectable()
|
|
120
|
-
* export class MyMessageApi extends AXConversationMessageApi {
|
|
121
|
-
* constructor(private http: HttpClient) {
|
|
122
|
-
* super();
|
|
123
|
-
* }
|
|
124
|
-
*
|
|
125
|
-
* async sendMessage(command: AXConversationSendMessageCommand): Promise<AXConversationMessage> {
|
|
126
|
-
* const response = await this.http.post('/api/messages', command).toPromise();
|
|
127
|
-
* return response;
|
|
128
|
-
* }
|
|
129
|
-
*
|
|
130
|
-
* // ... implement other methods
|
|
131
|
-
* }
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
class AXConversationMessageApi {
|
|
135
|
-
// =====================
|
|
136
|
-
// Error Handling Helper
|
|
137
|
-
// =====================
|
|
138
|
-
/**
|
|
139
|
-
* Create an API error
|
|
140
|
-
* Helper method for consistent error creation
|
|
141
|
-
*
|
|
142
|
-
* @param code - Error code
|
|
143
|
-
* @param message - Error message
|
|
144
|
-
* @param statusCode - HTTP status code
|
|
145
|
-
* @param details - Additional details
|
|
146
|
-
* @returns API error object
|
|
147
|
-
*/
|
|
148
|
-
createError(code, message, statusCode, details) {
|
|
149
|
-
return {
|
|
150
|
-
code,
|
|
151
|
-
message,
|
|
152
|
-
statusCode,
|
|
153
|
-
details,
|
|
154
|
-
timestamp: new Date(),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Abstract Real-time Events API
|
|
161
|
-
* Handle WebSocket/SSE connections and real-time event subscriptions
|
|
162
|
-
*/
|
|
163
|
-
/**
|
|
164
|
-
* Abstract Real-time Events API
|
|
165
|
-
*
|
|
166
|
-
* Implement this abstract class to handle real-time event subscriptions.
|
|
167
|
-
* Supports WebSocket, SSE, or any other real-time protocol.
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* ```typescript
|
|
171
|
-
* @Injectable()
|
|
172
|
-
* export class MyRealtimeApi extends AXConversationRealtimeApi {
|
|
173
|
-
* private socket: WebSocket;
|
|
174
|
-
* private connectionStatus$ = new BehaviorSubject<AXConversationConnectionStatus>('disconnected');
|
|
175
|
-
*
|
|
176
|
-
* async connect(options?: AXConversationConnectionOptions): Promise<void> {
|
|
177
|
-
* this.socket = new WebSocket(options?.url || 'ws://localhost:3000');
|
|
178
|
-
* this.socket.onopen = () => this.connectionStatus$.next('connected');
|
|
179
|
-
* // ... handle other events
|
|
180
|
-
* }
|
|
181
|
-
*
|
|
182
|
-
* subscribeToMessages(conversationId: string): Observable<AXConversationMessage> {
|
|
183
|
-
* return new Observable(subscriber => {
|
|
184
|
-
* this.socket.addEventListener('message', (event) => {
|
|
185
|
-
* const data = JSON.parse(event.data);
|
|
186
|
-
* if (data.type === 'message' && data.conversationId === conversationId) {
|
|
187
|
-
* subscriber.next(data.message);
|
|
188
|
-
* }
|
|
189
|
-
* });
|
|
190
|
-
* });
|
|
191
|
-
* }
|
|
192
|
-
*
|
|
193
|
-
* // ... implement other methods
|
|
194
|
-
* }
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
class AXConversationRealtimeApi {
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Abstract User Management API
|
|
202
|
-
* Handle user profile management and user operations for conversations
|
|
203
|
-
*/
|
|
204
|
-
/**
|
|
205
|
-
* Abstract User Management API
|
|
206
|
-
*
|
|
207
|
-
* Implement this abstract class to handle user management for conversations.
|
|
208
|
-
*
|
|
209
|
-
* @example
|
|
210
|
-
* ```typescript
|
|
211
|
-
* @Injectable()
|
|
212
|
-
* export class MyUserApi extends AXConversationUserApi {
|
|
213
|
-
* constructor(private http: HttpClient) {
|
|
214
|
-
* super();
|
|
215
|
-
* }
|
|
216
|
-
*
|
|
217
|
-
* async getCurrentUser(): Promise<AXConversationParticipant> {
|
|
218
|
-
* const response = await this.http.get('/api/users/me').toPromise();
|
|
219
|
-
* return response;
|
|
220
|
-
* }
|
|
221
|
-
*
|
|
222
|
-
* // ... implement other methods
|
|
223
|
-
* }
|
|
224
|
-
* ```
|
|
225
|
-
*/
|
|
226
|
-
class AXConversationUserApi {
|
|
227
|
-
// =====================
|
|
228
|
-
// Error Handling Helper
|
|
229
|
-
// =====================
|
|
230
|
-
/**
|
|
231
|
-
* Create an API error
|
|
232
|
-
* Helper method for consistent error creation
|
|
233
|
-
*
|
|
234
|
-
* @param code - Error code
|
|
235
|
-
* @param message - Error message
|
|
236
|
-
* @param statusCode - HTTP status code
|
|
237
|
-
* @param details - Additional details
|
|
238
|
-
* @returns API error object
|
|
239
|
-
*/
|
|
240
|
-
createError(code, message, statusCode, details) {
|
|
241
|
-
return {
|
|
242
|
-
code,
|
|
243
|
-
message,
|
|
244
|
-
statusCode,
|
|
245
|
-
details,
|
|
246
|
-
timestamp: new Date(),
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* API Services
|
|
253
|
-
* Export all abstract API classes and types
|
|
254
|
-
*/
|
|
255
|
-
// Shared types
|
|
256
|
-
|
|
257
|
-
/** Lightweight catalog ids — safe to import from plugin definitions without pulling pickers. */
|
|
258
|
-
const AX_CONVERSATION_IMAGE_CATALOG = 'conversation-image';
|
|
259
|
-
const AX_CONVERSATION_VIDEO_CATALOG = 'conversation-video';
|
|
260
|
-
const AX_CONVERSATION_AUDIO_CATALOG = 'conversation-audio';
|
|
261
|
-
const AX_CONVERSATION_FILE_CATALOG = 'conversation-file';
|
|
262
|
-
const AX_CONVERSATION_VOICE_CATALOG = 'conversation-voice';
|
|
263
|
-
/** Catalog names for conversation media message types (no picker module imports). */
|
|
264
|
-
const AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE = {
|
|
265
|
-
image: AX_CONVERSATION_IMAGE_CATALOG,
|
|
266
|
-
video: AX_CONVERSATION_VIDEO_CATALOG,
|
|
267
|
-
audio: AX_CONVERSATION_AUDIO_CATALOG,
|
|
268
|
-
file: AX_CONVERSATION_FILE_CATALOG,
|
|
269
|
-
voice: AX_CONVERSATION_VOICE_CATALOG,
|
|
270
|
-
sticker: AX_CONVERSATION_IMAGE_CATALOG,
|
|
271
|
-
};
|
|
272
|
-
/** Resolves {@link AXConversationMessage.fileType} from command or message type. */
|
|
273
|
-
function resolveConversationMessageFileType(type, fileType) {
|
|
274
|
-
return fileType ?? AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE[type];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
let modulePromise = null;
|
|
278
|
-
function loadSendMessageMedia() {
|
|
279
|
-
modulePromise ??= Promise.resolve().then(function () { return sendMessageMedia; });
|
|
280
|
-
return modulePromise;
|
|
281
|
-
}
|
|
282
|
-
async function applyLocalPreview$1(type, payload, localUrl, mimeType = 'application/octet-stream') {
|
|
283
|
-
const mod = await loadSendMessageMedia();
|
|
284
|
-
return mod.applyLocalPreview(type, payload, localUrl, mimeType);
|
|
285
|
-
}
|
|
286
|
-
async function createLocalPreviewUrl$1(fileService, platformId, source, messageType) {
|
|
287
|
-
const mod = await loadSendMessageMedia();
|
|
288
|
-
return mod.createLocalPreviewUrl(fileService, platformId, source, messageType);
|
|
289
|
-
}
|
|
290
|
-
async function mergeUploadResult$1(type, payload, result) {
|
|
291
|
-
const mod = await loadSendMessageMedia();
|
|
292
|
-
return mod.mergeUploadResult(type, payload, result);
|
|
293
|
-
}
|
|
294
|
-
async function revokeObjectUrl$1(platformId, url) {
|
|
295
|
-
const mod = await loadSendMessageMedia();
|
|
296
|
-
mod.revokeObjectUrl(platformId, url);
|
|
297
|
-
}
|
|
298
|
-
async function toUploaderReference$2(payload) {
|
|
299
|
-
const mod = await loadSendMessageMedia();
|
|
300
|
-
return mod.toUploaderReference(payload);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const AX_CONVERSATION_MEDIA_TYPES = new Set(['image', 'video', 'audio', 'file']);
|
|
304
|
-
const normalizers = {};
|
|
305
|
-
const loading = new Map();
|
|
306
|
-
function isMediaPayloadType(type) {
|
|
307
|
-
return AX_CONVERSATION_MEDIA_TYPES.has(type);
|
|
308
|
-
}
|
|
309
|
-
async function loadNormalizer(type) {
|
|
310
|
-
if (normalizers[type]) {
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
const pending = loading.get(type);
|
|
314
|
-
if (pending) {
|
|
315
|
-
await pending;
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
const promise = (async () => {
|
|
319
|
-
switch (type) {
|
|
320
|
-
case 'image': {
|
|
321
|
-
const mod = await Promise.resolve().then(function () { return image_payload; });
|
|
322
|
-
normalizers.image = mod.normalizeImagePayload;
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
case 'video': {
|
|
326
|
-
const mod = await Promise.resolve().then(function () { return video_payload; });
|
|
327
|
-
normalizers.video = mod.normalizeVideoPayload;
|
|
328
|
-
break;
|
|
329
|
-
}
|
|
330
|
-
case 'audio': {
|
|
331
|
-
const mod = await Promise.resolve().then(function () { return audio_payload; });
|
|
332
|
-
normalizers.audio = mod.normalizeAudioPayload;
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
case 'file': {
|
|
336
|
-
const mod = await Promise.resolve().then(function () { return file_payload; });
|
|
337
|
-
normalizers.file = mod.normalizeFilePayload;
|
|
338
|
-
break;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
})();
|
|
342
|
-
loading.set(type, promise);
|
|
343
|
-
await promise;
|
|
344
|
-
}
|
|
345
|
-
/** Normalize payload; loads media normalizers on demand. */
|
|
346
|
-
async function normalizeMessagePayloadAsync(payload) {
|
|
347
|
-
if (!isMediaPayloadType(payload.type)) {
|
|
348
|
-
return payload;
|
|
349
|
-
}
|
|
350
|
-
await loadNormalizer(payload.type);
|
|
351
|
-
const normalize = normalizers[payload.type];
|
|
352
|
-
return normalize ? normalize(payload) : payload;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Sync normalize — uses cached normalizers when available.
|
|
356
|
-
* Media payloads pass through until a normalizer chunk has loaded (renderers normalize on display).
|
|
357
|
-
*/
|
|
358
|
-
function normalizeMessagePayload(payload) {
|
|
359
|
-
if (!isMediaPayloadType(payload.type)) {
|
|
360
|
-
return payload;
|
|
361
|
-
}
|
|
362
|
-
const normalize = normalizers[payload.type];
|
|
363
|
-
if (normalize) {
|
|
364
|
-
return normalize(payload);
|
|
365
|
-
}
|
|
366
|
-
void loadNormalizer(payload.type);
|
|
367
|
-
return payload;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
58
|
/**
|
|
371
59
|
* Conversation Configuration Interface
|
|
372
60
|
* Centralized configuration values to avoid magic numbers
|
|
@@ -430,12 +118,15 @@ const AX_DEFAULT_CONVERSATION_CONFIG = {
|
|
|
430
118
|
maxFilesPerMessage: 3,
|
|
431
119
|
// Intersection Observer
|
|
432
120
|
messageReadThreshold: 0.3,
|
|
121
|
+
messageReadDebounce: 400,
|
|
122
|
+
markConversationReadOnSelect: true,
|
|
433
123
|
// Message list
|
|
434
124
|
messageListBackground: '',
|
|
435
125
|
features: {
|
|
436
126
|
mediaPickers: [...AX_ALL_CONVERSATION_MEDIA_PICKERS],
|
|
437
127
|
composerTabs: [...AX_ALL_CONVERSATION_COMPOSER_TABS],
|
|
438
128
|
},
|
|
129
|
+
debugApiLogging: false,
|
|
439
130
|
};
|
|
440
131
|
/**
|
|
441
132
|
* Helper function to merge user config with defaults
|
|
@@ -511,50 +202,292 @@ const AX_CONVERSATION_DEFAULT_MESSAGE_ACTIONS = new InjectionToken('AX_CONVERSAT
|
|
|
511
202
|
* Default composer tabs configuration
|
|
512
203
|
* Provide this token to override default composer tabs (emoji, stickers, etc.)
|
|
513
204
|
*/
|
|
514
|
-
const AX_CONVERSATION_DEFAULT_COMPOSER_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_TABS', {
|
|
515
|
-
providedIn: 'root',
|
|
516
|
-
factory: () => [],
|
|
517
|
-
});
|
|
205
|
+
const AX_CONVERSATION_DEFAULT_COMPOSER_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_TABS', {
|
|
206
|
+
providedIn: 'root',
|
|
207
|
+
factory: () => [],
|
|
208
|
+
});
|
|
209
|
+
/**
|
|
210
|
+
* Default composer actions configuration
|
|
211
|
+
* Provide this token to override default composer actions (attach, voice, etc.)
|
|
212
|
+
*/
|
|
213
|
+
const AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS', {
|
|
214
|
+
providedIn: 'root',
|
|
215
|
+
factory: () => [],
|
|
216
|
+
});
|
|
217
|
+
/**
|
|
218
|
+
* Default conversation tabs configuration
|
|
219
|
+
* Provide this token to override default conversation tabs (all, private, groups, etc.)
|
|
220
|
+
*/
|
|
221
|
+
const AX_CONVERSATION_DEFAULT_CONVERSATION_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_CONVERSATION_TABS', {
|
|
222
|
+
providedIn: 'root',
|
|
223
|
+
factory: () => [],
|
|
224
|
+
});
|
|
225
|
+
/**
|
|
226
|
+
* Default info bar actions configuration
|
|
227
|
+
* Provide this token to override default info bar actions (mute, archive, block, etc.)
|
|
228
|
+
*/
|
|
229
|
+
const AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS', {
|
|
230
|
+
providedIn: 'root',
|
|
231
|
+
factory: () => [],
|
|
232
|
+
});
|
|
233
|
+
/**
|
|
234
|
+
* Default conversation item actions configuration
|
|
235
|
+
* Provide this token to override default conversation item actions (mute, delete, archive, etc.)
|
|
236
|
+
*/
|
|
237
|
+
const AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS', {
|
|
238
|
+
providedIn: 'root',
|
|
239
|
+
factory: () => [],
|
|
240
|
+
});
|
|
241
|
+
/**
|
|
242
|
+
* Complete registry configuration token
|
|
243
|
+
* Provide this for comprehensive registry configuration
|
|
244
|
+
*/
|
|
245
|
+
const AX_CONVERSATION_REGISTRY_CONFIG = new InjectionToken('AX_CONVERSATION_REGISTRY_CONFIG', {
|
|
246
|
+
providedIn: 'root',
|
|
247
|
+
factory: () => ({}),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* API call logger for AXConversationService.
|
|
252
|
+
* Enabled via `debugApiLogging` in conversation config.
|
|
253
|
+
*/
|
|
254
|
+
const loggerFn = console.log;
|
|
255
|
+
class AXConversationApiLoggerService {
|
|
256
|
+
constructor() {
|
|
257
|
+
this.config = inject(AX_CONVERSATION_CONFIG);
|
|
258
|
+
}
|
|
259
|
+
log(entry) {
|
|
260
|
+
if (!this.config.debugApiLogging) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
loggerFn('[AXConversation API]', entry);
|
|
264
|
+
}
|
|
265
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationApiLoggerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
266
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationApiLoggerService }); }
|
|
267
|
+
}
|
|
268
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationApiLoggerService, decorators: [{
|
|
269
|
+
type: Injectable
|
|
270
|
+
}] });
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Abstract Conversation Management API
|
|
274
|
+
* Handle conversation CRUD operations, participants, and settings
|
|
275
|
+
*/
|
|
276
|
+
/**
|
|
277
|
+
* Abstract Conversation Management API
|
|
278
|
+
*
|
|
279
|
+
* Implement this abstract class to handle conversation operations.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* @Injectable()
|
|
284
|
+
* export class MyConversationApi extends AXConversationManagementApi {
|
|
285
|
+
* constructor(private http: HttpClient) {
|
|
286
|
+
* super();
|
|
287
|
+
* }
|
|
288
|
+
*
|
|
289
|
+
* async createConversation(data: AXConversationCreateData): Promise<AXConversation> {
|
|
290
|
+
* const response = await this.http.post('/api/conversations', data).toPromise();
|
|
291
|
+
* return response;
|
|
292
|
+
* }
|
|
293
|
+
*
|
|
294
|
+
* // ... implement other methods
|
|
295
|
+
* }
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
class AXConversationApi {
|
|
299
|
+
// =====================
|
|
300
|
+
// Error Handling Helper
|
|
301
|
+
// =====================
|
|
302
|
+
/**
|
|
303
|
+
* Create an API error
|
|
304
|
+
* Helper method for consistent error creation
|
|
305
|
+
*
|
|
306
|
+
* @param code - Error code
|
|
307
|
+
* @param message - Error message
|
|
308
|
+
* @param statusCode - HTTP status code
|
|
309
|
+
* @param details - Additional details
|
|
310
|
+
* @returns API error object
|
|
311
|
+
*/
|
|
312
|
+
createError(code, message, statusCode, details) {
|
|
313
|
+
return {
|
|
314
|
+
code,
|
|
315
|
+
message,
|
|
316
|
+
statusCode,
|
|
317
|
+
details,
|
|
318
|
+
timestamp: new Date(),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Abstract Message Management API
|
|
325
|
+
* Handle message CRUD operations, reactions, and interactions
|
|
326
|
+
*/
|
|
327
|
+
/**
|
|
328
|
+
* Abstract Message Management API
|
|
329
|
+
*
|
|
330
|
+
* Implement this abstract class to handle message operations.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* @Injectable()
|
|
335
|
+
* export class MyMessageApi extends AXConversationMessageApi {
|
|
336
|
+
* constructor(private http: HttpClient) {
|
|
337
|
+
* super();
|
|
338
|
+
* }
|
|
339
|
+
*
|
|
340
|
+
* async sendMessage(command: AXConversationSendMessageCommand): Promise<AXConversationMessage> {
|
|
341
|
+
* const response = await this.http.post('/api/messages', command).toPromise();
|
|
342
|
+
* return response;
|
|
343
|
+
* }
|
|
344
|
+
*
|
|
345
|
+
* // ... implement other methods
|
|
346
|
+
* }
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
class AXConversationMessageApi {
|
|
350
|
+
// =====================
|
|
351
|
+
// Error Handling Helper
|
|
352
|
+
// =====================
|
|
353
|
+
/**
|
|
354
|
+
* Create an API error
|
|
355
|
+
* Helper method for consistent error creation
|
|
356
|
+
*
|
|
357
|
+
* @param code - Error code
|
|
358
|
+
* @param message - Error message
|
|
359
|
+
* @param statusCode - HTTP status code
|
|
360
|
+
* @param details - Additional details
|
|
361
|
+
* @returns API error object
|
|
362
|
+
*/
|
|
363
|
+
createError(code, message, statusCode, details) {
|
|
364
|
+
return {
|
|
365
|
+
code,
|
|
366
|
+
message,
|
|
367
|
+
statusCode,
|
|
368
|
+
details,
|
|
369
|
+
timestamp: new Date(),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
518
374
|
/**
|
|
519
|
-
*
|
|
520
|
-
*
|
|
375
|
+
* Abstract Real-time Events API
|
|
376
|
+
* Handle WebSocket/SSE connections and real-time event subscriptions
|
|
521
377
|
*/
|
|
522
|
-
const AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS', {
|
|
523
|
-
providedIn: 'root',
|
|
524
|
-
factory: () => [],
|
|
525
|
-
});
|
|
526
378
|
/**
|
|
527
|
-
*
|
|
528
|
-
*
|
|
379
|
+
* Abstract Real-time Events API
|
|
380
|
+
*
|
|
381
|
+
* Implement this abstract class to handle real-time event subscriptions.
|
|
382
|
+
* Supports WebSocket, SSE, or any other real-time protocol.
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* @Injectable()
|
|
387
|
+
* export class MyRealtimeApi extends AXConversationRealtimeApi {
|
|
388
|
+
* private socket: WebSocket;
|
|
389
|
+
* private connectionStatus$ = new BehaviorSubject<AXConversationConnectionStatus>('disconnected');
|
|
390
|
+
*
|
|
391
|
+
* async connect(options?: AXConversationConnectionOptions): Promise<void> {
|
|
392
|
+
* this.socket = new WebSocket(options?.url || 'ws://localhost:3000');
|
|
393
|
+
* this.socket.onopen = () => this.connectionStatus$.next('connected');
|
|
394
|
+
* // ... handle other events
|
|
395
|
+
* }
|
|
396
|
+
*
|
|
397
|
+
* subscribeToMessages(conversationId: string): Observable<AXConversationMessage> {
|
|
398
|
+
* return new Observable(subscriber => {
|
|
399
|
+
* this.socket.addEventListener('message', (event) => {
|
|
400
|
+
* const data = JSON.parse(event.data);
|
|
401
|
+
* if (data.type === 'message' && data.conversationId === conversationId) {
|
|
402
|
+
* subscriber.next(data.message);
|
|
403
|
+
* }
|
|
404
|
+
* });
|
|
405
|
+
* });
|
|
406
|
+
* }
|
|
407
|
+
*
|
|
408
|
+
* // ... implement other methods
|
|
409
|
+
* }
|
|
410
|
+
* ```
|
|
529
411
|
*/
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
});
|
|
412
|
+
class AXConversationRealtimeApi {
|
|
413
|
+
}
|
|
414
|
+
|
|
534
415
|
/**
|
|
535
|
-
*
|
|
536
|
-
*
|
|
416
|
+
* Abstract User Management API
|
|
417
|
+
* Handle user profile management and user operations for conversations
|
|
537
418
|
*/
|
|
538
|
-
const AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS', {
|
|
539
|
-
providedIn: 'root',
|
|
540
|
-
factory: () => [],
|
|
541
|
-
});
|
|
542
419
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
420
|
+
* Abstract User Management API
|
|
421
|
+
*
|
|
422
|
+
* Implement this abstract class to handle user management for conversations.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```typescript
|
|
426
|
+
* @Injectable()
|
|
427
|
+
* export class MyUserApi extends AXConversationUserApi {
|
|
428
|
+
* constructor(private http: HttpClient) {
|
|
429
|
+
* super();
|
|
430
|
+
* }
|
|
431
|
+
*
|
|
432
|
+
* async getCurrentUser(): Promise<AXConversationParticipant> {
|
|
433
|
+
* const response = await this.http.get('/api/users/me').toPromise();
|
|
434
|
+
* return response;
|
|
435
|
+
* }
|
|
436
|
+
*
|
|
437
|
+
* // ... implement other methods
|
|
438
|
+
* }
|
|
439
|
+
* ```
|
|
545
440
|
*/
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
441
|
+
class AXConversationUserApi {
|
|
442
|
+
// =====================
|
|
443
|
+
// Error Handling Helper
|
|
444
|
+
// =====================
|
|
445
|
+
/**
|
|
446
|
+
* Create an API error
|
|
447
|
+
* Helper method for consistent error creation
|
|
448
|
+
*
|
|
449
|
+
* @param code - Error code
|
|
450
|
+
* @param message - Error message
|
|
451
|
+
* @param statusCode - HTTP status code
|
|
452
|
+
* @param details - Additional details
|
|
453
|
+
* @returns API error object
|
|
454
|
+
*/
|
|
455
|
+
createError(code, message, statusCode, details) {
|
|
456
|
+
return {
|
|
457
|
+
code,
|
|
458
|
+
message,
|
|
459
|
+
statusCode,
|
|
460
|
+
details,
|
|
461
|
+
timestamp: new Date(),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
550
466
|
/**
|
|
551
|
-
*
|
|
552
|
-
*
|
|
467
|
+
* API Services
|
|
468
|
+
* Export all abstract API classes and types
|
|
553
469
|
*/
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
470
|
+
// Shared types
|
|
471
|
+
|
|
472
|
+
/** Lightweight catalog ids — safe to import from plugin definitions without pulling pickers. */
|
|
473
|
+
const AX_CONVERSATION_IMAGE_CATALOG = 'conversation-image';
|
|
474
|
+
const AX_CONVERSATION_VIDEO_CATALOG = 'conversation-video';
|
|
475
|
+
const AX_CONVERSATION_AUDIO_CATALOG = 'conversation-audio';
|
|
476
|
+
const AX_CONVERSATION_FILE_CATALOG = 'conversation-file';
|
|
477
|
+
const AX_CONVERSATION_VOICE_CATALOG = 'conversation-voice';
|
|
478
|
+
/** Catalog names for conversation media message types (no picker module imports). */
|
|
479
|
+
const AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE = {
|
|
480
|
+
image: AX_CONVERSATION_IMAGE_CATALOG,
|
|
481
|
+
video: AX_CONVERSATION_VIDEO_CATALOG,
|
|
482
|
+
audio: AX_CONVERSATION_AUDIO_CATALOG,
|
|
483
|
+
file: AX_CONVERSATION_FILE_CATALOG,
|
|
484
|
+
voice: AX_CONVERSATION_VOICE_CATALOG,
|
|
485
|
+
sticker: AX_CONVERSATION_IMAGE_CATALOG,
|
|
486
|
+
};
|
|
487
|
+
/** Resolves {@link AXConversationMessage.fileType} from command or message type. */
|
|
488
|
+
function resolveConversationMessageFileType(type, fileType) {
|
|
489
|
+
return fileType ?? AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE[type];
|
|
490
|
+
}
|
|
558
491
|
|
|
559
492
|
class AXConversationMessageUtilsService {
|
|
560
493
|
/**
|
|
@@ -962,6 +895,73 @@ function resolveConversationForViewer(conversation, currentUserId) {
|
|
|
962
895
|
};
|
|
963
896
|
}
|
|
964
897
|
|
|
898
|
+
const AX_CONVERSATION_MEDIA_TYPES = new Set(['image', 'video', 'audio', 'file']);
|
|
899
|
+
const normalizers = {};
|
|
900
|
+
const loading = new Map();
|
|
901
|
+
function isMediaPayloadType(type) {
|
|
902
|
+
return AX_CONVERSATION_MEDIA_TYPES.has(type);
|
|
903
|
+
}
|
|
904
|
+
async function loadNormalizer(type) {
|
|
905
|
+
if (normalizers[type]) {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const pending = loading.get(type);
|
|
909
|
+
if (pending) {
|
|
910
|
+
await pending;
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const promise = (async () => {
|
|
914
|
+
switch (type) {
|
|
915
|
+
case 'image': {
|
|
916
|
+
const mod = await Promise.resolve().then(function () { return image_payload; });
|
|
917
|
+
normalizers.image = mod.normalizeImagePayload;
|
|
918
|
+
break;
|
|
919
|
+
}
|
|
920
|
+
case 'video': {
|
|
921
|
+
const mod = await Promise.resolve().then(function () { return video_payload; });
|
|
922
|
+
normalizers.video = mod.normalizeVideoPayload;
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
case 'audio': {
|
|
926
|
+
const mod = await Promise.resolve().then(function () { return audio_payload; });
|
|
927
|
+
normalizers.audio = mod.normalizeAudioPayload;
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
case 'file': {
|
|
931
|
+
const mod = await Promise.resolve().then(function () { return file_payload; });
|
|
932
|
+
normalizers.file = mod.normalizeFilePayload;
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
})();
|
|
937
|
+
loading.set(type, promise);
|
|
938
|
+
await promise;
|
|
939
|
+
}
|
|
940
|
+
/** Normalize payload; loads media normalizers on demand. */
|
|
941
|
+
async function normalizeMessagePayloadAsync(payload) {
|
|
942
|
+
if (!isMediaPayloadType(payload.type)) {
|
|
943
|
+
return payload;
|
|
944
|
+
}
|
|
945
|
+
await loadNormalizer(payload.type);
|
|
946
|
+
const normalize = normalizers[payload.type];
|
|
947
|
+
return normalize ? normalize(payload) : payload;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Sync normalize — uses cached normalizers when available.
|
|
951
|
+
* Media payloads pass through until a normalizer chunk has loaded (renderers normalize on display).
|
|
952
|
+
*/
|
|
953
|
+
function normalizeMessagePayload(payload) {
|
|
954
|
+
if (!isMediaPayloadType(payload.type)) {
|
|
955
|
+
return payload;
|
|
956
|
+
}
|
|
957
|
+
const normalize = normalizers[payload.type];
|
|
958
|
+
if (normalize) {
|
|
959
|
+
return normalize(payload);
|
|
960
|
+
}
|
|
961
|
+
void loadNormalizer(payload.type);
|
|
962
|
+
return payload;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
965
|
/**
|
|
966
966
|
* Validation Utilities
|
|
967
967
|
* Centralized validation functions for messages and user input
|
|
@@ -1331,6 +1331,32 @@ function validateLongitude(longitude) {
|
|
|
1331
1331
|
return { valid: true };
|
|
1332
1332
|
}
|
|
1333
1333
|
|
|
1334
|
+
let modulePromise = null;
|
|
1335
|
+
function loadSendMessageMedia() {
|
|
1336
|
+
modulePromise ??= Promise.resolve().then(function () { return sendMessageMedia; });
|
|
1337
|
+
return modulePromise;
|
|
1338
|
+
}
|
|
1339
|
+
async function applyLocalPreview$1(type, payload, localUrl, mimeType = 'application/octet-stream') {
|
|
1340
|
+
const mod = await loadSendMessageMedia();
|
|
1341
|
+
return mod.applyLocalPreview(type, payload, localUrl, mimeType);
|
|
1342
|
+
}
|
|
1343
|
+
async function createLocalPreviewUrl$1(fileService, platformId, source, messageType) {
|
|
1344
|
+
const mod = await loadSendMessageMedia();
|
|
1345
|
+
return mod.createLocalPreviewUrl(fileService, platformId, source, messageType);
|
|
1346
|
+
}
|
|
1347
|
+
async function mergeUploadResult$1(type, payload, result) {
|
|
1348
|
+
const mod = await loadSendMessageMedia();
|
|
1349
|
+
return mod.mergeUploadResult(type, payload, result);
|
|
1350
|
+
}
|
|
1351
|
+
async function revokeObjectUrl$1(platformId, url) {
|
|
1352
|
+
const mod = await loadSendMessageMedia();
|
|
1353
|
+
mod.revokeObjectUrl(platformId, url);
|
|
1354
|
+
}
|
|
1355
|
+
async function toUploaderReference$2(payload) {
|
|
1356
|
+
const mod = await loadSendMessageMedia();
|
|
1357
|
+
return mod.toUploaderReference(payload);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1334
1360
|
/**
|
|
1335
1361
|
* In-memory conversation and message graph (signal-based).
|
|
1336
1362
|
* Plain class — not DI-registered; instantiated by `AXConversationService`.
|
|
@@ -2960,6 +2986,7 @@ class AXConversationService {
|
|
|
2960
2986
|
this.fileService = inject(AXFileService);
|
|
2961
2987
|
this.platformId = inject(PLATFORM_ID);
|
|
2962
2988
|
this.errorHandler = inject(AXConversationErrorHandlerService);
|
|
2989
|
+
this.apiLogger = inject(AXConversationApiLoggerService);
|
|
2963
2990
|
this.dialogService = inject(AXDialogService);
|
|
2964
2991
|
this.popupService = inject(AXPopupService);
|
|
2965
2992
|
this.translation = inject(AXTranslationService);
|
|
@@ -3040,19 +3067,48 @@ class AXConversationService {
|
|
|
3040
3067
|
this.onTypingIndicator = this._typingIndicator$.asObservable();
|
|
3041
3068
|
this.onPresenceChange = this._presenceUpdate$.asObservable();
|
|
3042
3069
|
// Message count refresh event (emits messageId when counts need refresh)
|
|
3043
|
-
this._messageCountRefresh$ = new Subject();
|
|
3044
|
-
this.onMessageCountRefresh = this._messageCountRefresh$.asObservable();
|
|
3045
3070
|
// Current user cache
|
|
3046
3071
|
this._currentUser = signal(null, /* @ts-ignore */
|
|
3047
3072
|
...(ngDevMode ? [{ debugName: "_currentUser" }] : /* istanbul ignore next */ []));
|
|
3048
3073
|
this.currentUser = this._currentUser.asReadonly();
|
|
3074
|
+
/** One-time async initialization (connect, user, conversations, realtime). */
|
|
3075
|
+
this.initPromise = null;
|
|
3076
|
+
/** Generation counter — stale page-0 loads are ignored after conversation switches. */
|
|
3077
|
+
this.messageLoadGeneration = 0;
|
|
3078
|
+
/** Batched read-receipt queue keyed by conversation ID. */
|
|
3079
|
+
this.readQueue = new Map();
|
|
3080
|
+
/** Session cache for available reaction emojis. */
|
|
3081
|
+
this.availableReactionsCache = null;
|
|
3082
|
+
this.availableReactionsPromise = null;
|
|
3049
3083
|
this.destroyRef.onDestroy(() => {
|
|
3050
3084
|
this._conversationSwitch$.next();
|
|
3051
3085
|
this._conversationSwitch$.complete();
|
|
3052
3086
|
this.destroy$.next();
|
|
3053
3087
|
this.destroy$.complete();
|
|
3088
|
+
if (this.readFlushHandle) {
|
|
3089
|
+
clearTimeout(this.readFlushHandle);
|
|
3090
|
+
}
|
|
3091
|
+
void this.flushReadQueue();
|
|
3054
3092
|
});
|
|
3055
|
-
|
|
3093
|
+
}
|
|
3094
|
+
/** Log and invoke an async API method from AXConversationService. */
|
|
3095
|
+
callApi(serviceMethod, api, apiMethod, fn, ...context) {
|
|
3096
|
+
this.apiLogger.log({ serviceMethod, api, apiMethod, context });
|
|
3097
|
+
return fn();
|
|
3098
|
+
}
|
|
3099
|
+
/** Log a sync API invocation (e.g. realtime observable subscriptions). */
|
|
3100
|
+
logApi(serviceMethod, api, apiMethod, ...context) {
|
|
3101
|
+
this.apiLogger.log({ serviceMethod, api, apiMethod, context });
|
|
3102
|
+
}
|
|
3103
|
+
/**
|
|
3104
|
+
* Ensures the service is initialized exactly once.
|
|
3105
|
+
* Safe to call from container components or before any public API usage.
|
|
3106
|
+
*/
|
|
3107
|
+
ensureInitialized() {
|
|
3108
|
+
if (!this.initPromise) {
|
|
3109
|
+
this.initPromise = this.initializeService();
|
|
3110
|
+
}
|
|
3111
|
+
return this.initPromise;
|
|
3056
3112
|
}
|
|
3057
3113
|
/**
|
|
3058
3114
|
* Initialize the service
|
|
@@ -3062,10 +3118,10 @@ class AXConversationService {
|
|
|
3062
3118
|
try {
|
|
3063
3119
|
// Connect to real-time API (optional)
|
|
3064
3120
|
if (this.realtimeApi) {
|
|
3065
|
-
await this.realtimeApi.connect();
|
|
3121
|
+
await this.callApi('initializeService', 'realtimeApi', 'connect', () => this.realtimeApi.connect());
|
|
3066
3122
|
}
|
|
3067
3123
|
// Load current user
|
|
3068
|
-
const currentUser = await this.userApi.getCurrentUser();
|
|
3124
|
+
const currentUser = await this.callApi('initializeService', 'userApi', 'getCurrentUser', () => this.userApi.getCurrentUser());
|
|
3069
3125
|
this._currentUser.set(currentUser);
|
|
3070
3126
|
// Subscribe to real-time events
|
|
3071
3127
|
this.subscribeToEvents();
|
|
@@ -3087,6 +3143,7 @@ class AXConversationService {
|
|
|
3087
3143
|
return;
|
|
3088
3144
|
}
|
|
3089
3145
|
// Subscribe to message updates
|
|
3146
|
+
this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToMessageUpdates');
|
|
3090
3147
|
this.realtimeApi
|
|
3091
3148
|
.subscribeToMessageUpdates()
|
|
3092
3149
|
.pipe(takeUntil(this.destroy$), catchError((error) => {
|
|
@@ -3096,10 +3153,9 @@ class AXConversationService {
|
|
|
3096
3153
|
.subscribe((message) => {
|
|
3097
3154
|
this.handleMessageUpdate(message);
|
|
3098
3155
|
this._messageUpdated$.next(message);
|
|
3099
|
-
// Check if this is a reply or forward and refresh counts
|
|
3100
|
-
this.handleMessageCountUpdate(message);
|
|
3101
3156
|
});
|
|
3102
3157
|
// Subscribe to message deletions
|
|
3158
|
+
this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToMessageDeletions');
|
|
3103
3159
|
this.realtimeApi
|
|
3104
3160
|
.subscribeToMessageDeletions()
|
|
3105
3161
|
.pipe(takeUntil(this.destroy$), catchError((error) => {
|
|
@@ -3111,6 +3167,7 @@ class AXConversationService {
|
|
|
3111
3167
|
this._messageDeleted$.next(messageId);
|
|
3112
3168
|
});
|
|
3113
3169
|
// Subscribe to typing indicators
|
|
3170
|
+
this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToTypingIndicators', '');
|
|
3114
3171
|
this.realtimeApi
|
|
3115
3172
|
.subscribeToTypingIndicators('')
|
|
3116
3173
|
.pipe(takeUntil(this.destroy$), catchError((error) => {
|
|
@@ -3122,6 +3179,7 @@ class AXConversationService {
|
|
|
3122
3179
|
this._typingIndicator$.next(indicator);
|
|
3123
3180
|
});
|
|
3124
3181
|
// Subscribe to presence updates
|
|
3182
|
+
this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToPresenceUpdates');
|
|
3125
3183
|
this.realtimeApi
|
|
3126
3184
|
.subscribeToPresenceUpdates()
|
|
3127
3185
|
.pipe(takeUntil(this.destroy$), catchError((error) => {
|
|
@@ -3133,6 +3191,7 @@ class AXConversationService {
|
|
|
3133
3191
|
this._presenceUpdate$.next(update);
|
|
3134
3192
|
});
|
|
3135
3193
|
// Subscribe to conversation updates (for badge updates, last message, etc.)
|
|
3194
|
+
this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToConversationUpdates');
|
|
3136
3195
|
this.realtimeApi
|
|
3137
3196
|
.subscribeToConversationUpdates()
|
|
3138
3197
|
.pipe(takeUntil(this.destroy$), catchError((error) => {
|
|
@@ -3144,6 +3203,7 @@ class AXConversationService {
|
|
|
3144
3203
|
});
|
|
3145
3204
|
// Global message stream — backs message list even when the backend only pushes
|
|
3146
3205
|
// conversation.lastMessage updates or when per-conversation subscribe races loadMessages.
|
|
3206
|
+
this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToMessages', '');
|
|
3147
3207
|
this.realtimeApi
|
|
3148
3208
|
.subscribeToMessages('')
|
|
3149
3209
|
.pipe(takeUntil(this.destroy$), catchError((error) => {
|
|
@@ -3163,10 +3223,11 @@ class AXConversationService {
|
|
|
3163
3223
|
this._loading.set(true);
|
|
3164
3224
|
this._error.set(null);
|
|
3165
3225
|
try {
|
|
3166
|
-
const
|
|
3226
|
+
const pagination = {
|
|
3167
3227
|
page: 0,
|
|
3168
3228
|
pageSize: this.config.conversationPageSize,
|
|
3169
|
-
}
|
|
3229
|
+
};
|
|
3230
|
+
const result = await this.callApi('loadConversations', 'conversationApi', 'getConversations', () => this.conversationApi.getConversations(pagination, undefined), pagination, undefined);
|
|
3170
3231
|
// Store conversations
|
|
3171
3232
|
this.state.setConversations(result.items);
|
|
3172
3233
|
this._conversationsPagination.set({
|
|
@@ -3188,11 +3249,13 @@ class AXConversationService {
|
|
|
3188
3249
|
* Returns true if there are more pages available
|
|
3189
3250
|
*/
|
|
3190
3251
|
async loadMoreConversations(page) {
|
|
3252
|
+
await this.ensureInitialized();
|
|
3191
3253
|
try {
|
|
3192
|
-
const
|
|
3254
|
+
const pagination = {
|
|
3193
3255
|
page,
|
|
3194
3256
|
pageSize: this.config.conversationPageSize,
|
|
3195
|
-
}
|
|
3257
|
+
};
|
|
3258
|
+
const result = await this.callApi('loadMoreConversations', 'conversationApi', 'getConversations', () => this.conversationApi.getConversations(pagination, undefined), pagination, undefined);
|
|
3196
3259
|
// Append new conversations to existing ones
|
|
3197
3260
|
if (result.items.length > 0) {
|
|
3198
3261
|
this.state.addConversations(result.items);
|
|
@@ -3210,16 +3273,14 @@ class AXConversationService {
|
|
|
3210
3273
|
}
|
|
3211
3274
|
}
|
|
3212
3275
|
/**
|
|
3213
|
-
* Select a conversation
|
|
3214
|
-
* Loads messages and subscribes to real-time updates
|
|
3215
|
-
* Note: Messages are now marked as read individually via intersection observer when viewed
|
|
3276
|
+
* Select a conversation and load page 0 of its message history.
|
|
3216
3277
|
*/
|
|
3217
3278
|
async selectConversation(conversationId) {
|
|
3279
|
+
await this.ensureInitialized();
|
|
3218
3280
|
try {
|
|
3219
|
-
// Cancel previous per-conversation subscriptions before switching
|
|
3220
3281
|
this._conversationSwitch$.next();
|
|
3282
|
+
this.messageLoadGeneration++;
|
|
3221
3283
|
this._activeConversationId.set(conversationId);
|
|
3222
|
-
// Load messages for this conversation (page 0 replaces cached messages for this chat)
|
|
3223
3284
|
this._loadingActiveMessages.set(true);
|
|
3224
3285
|
try {
|
|
3225
3286
|
await this.loadMessages(conversationId, 0);
|
|
@@ -3227,6 +3288,12 @@ class AXConversationService {
|
|
|
3227
3288
|
finally {
|
|
3228
3289
|
this._loadingActiveMessages.set(false);
|
|
3229
3290
|
}
|
|
3291
|
+
if (this.config.markConversationReadOnSelect) {
|
|
3292
|
+
const conversation = this.state.getConversation(conversationId);
|
|
3293
|
+
if (conversation && conversation.unreadCount > 0) {
|
|
3294
|
+
void this.markConversationAsRead(conversationId);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3230
3297
|
}
|
|
3231
3298
|
catch (error) {
|
|
3232
3299
|
this.errorHandler.handle(error, 'selectConversation', { conversationId });
|
|
@@ -3244,14 +3311,19 @@ class AXConversationService {
|
|
|
3244
3311
|
*/
|
|
3245
3312
|
async loadMessages(conversationId, page = 0) {
|
|
3246
3313
|
const empty = { items: [], hasMore: false, page };
|
|
3314
|
+
const loadGeneration = page === 0 ? this.messageLoadGeneration : null;
|
|
3247
3315
|
try {
|
|
3248
3316
|
const paginationState = this._messagesPaginationByConversation().get(conversationId);
|
|
3249
3317
|
const cursor = page > 0 ? paginationState?.nextCursor : undefined;
|
|
3250
|
-
const
|
|
3318
|
+
const messagePagination = {
|
|
3251
3319
|
page,
|
|
3252
3320
|
pageSize: this.config.messagePageSize,
|
|
3253
3321
|
cursor,
|
|
3254
|
-
}
|
|
3322
|
+
};
|
|
3323
|
+
const result = await this.callApi('loadMessages', 'messageApi', 'getMessages', () => this.messageApi.getMessages(conversationId, messagePagination), conversationId, messagePagination);
|
|
3324
|
+
if (page === 0 && loadGeneration !== null && loadGeneration !== this.messageLoadGeneration) {
|
|
3325
|
+
return empty;
|
|
3326
|
+
}
|
|
3255
3327
|
if (page === 0) {
|
|
3256
3328
|
this.state.setConversationMessages(conversationId, result.items);
|
|
3257
3329
|
}
|
|
@@ -3357,16 +3429,10 @@ class AXConversationService {
|
|
|
3357
3429
|
payload: await mergeUploadResult$1(command.type, sendCommand.payload, uploadResult),
|
|
3358
3430
|
};
|
|
3359
3431
|
this.state.updateMessage(tempMessageId, { payload: sendCommand.payload });
|
|
3360
|
-
const sentMessage = await this.messageApi.sendMessage(sendCommand);
|
|
3432
|
+
const sentMessage = await this.callApi('sendMessage', 'messageApi', 'sendMessage', () => this.messageApi.sendMessage(sendCommand), sendCommand);
|
|
3361
3433
|
this.state.deleteMessage(tempMessageId);
|
|
3362
3434
|
this.state.addMessage(sentMessage);
|
|
3363
3435
|
this.updateConversationLastMessage(sentMessage);
|
|
3364
|
-
if (sentMessage.forwarded && sentMessage.forwardedFrom?.messageId) {
|
|
3365
|
-
this._messageCountRefresh$.next({
|
|
3366
|
-
messageId: sentMessage.forwardedFrom.messageId,
|
|
3367
|
-
type: 'forward',
|
|
3368
|
-
});
|
|
3369
|
-
}
|
|
3370
3436
|
if (localPreviewUrl?.startsWith('blob:')) {
|
|
3371
3437
|
await revokeObjectUrl$1(this.platformId, localPreviewUrl);
|
|
3372
3438
|
}
|
|
@@ -3399,16 +3465,10 @@ class AXConversationService {
|
|
|
3399
3465
|
metadata: command.metadata,
|
|
3400
3466
|
};
|
|
3401
3467
|
this.state.addMessage(tempMessage);
|
|
3402
|
-
const sentMessage = await this.messageApi.sendMessage(sendCommand);
|
|
3468
|
+
const sentMessage = await this.callApi('sendMessage', 'messageApi', 'sendMessage', () => this.messageApi.sendMessage(sendCommand), sendCommand);
|
|
3403
3469
|
this.state.deleteMessage(tempMessageId);
|
|
3404
3470
|
this.state.addMessage(sentMessage);
|
|
3405
3471
|
this.updateConversationLastMessage(sentMessage);
|
|
3406
|
-
if (sentMessage.forwarded && sentMessage.forwardedFrom?.messageId) {
|
|
3407
|
-
this._messageCountRefresh$.next({
|
|
3408
|
-
messageId: sentMessage.forwardedFrom.messageId,
|
|
3409
|
-
type: 'forward',
|
|
3410
|
-
});
|
|
3411
|
-
}
|
|
3412
3472
|
}
|
|
3413
3473
|
catch (error) {
|
|
3414
3474
|
this.state.updateMessage(tempMessageId, { status: 'failed' });
|
|
@@ -3442,7 +3502,7 @@ class AXConversationService {
|
|
|
3442
3502
|
metadata: message.metadata,
|
|
3443
3503
|
};
|
|
3444
3504
|
// Send to server
|
|
3445
|
-
const sentMessage = await this.messageApi.sendMessage(command);
|
|
3505
|
+
const sentMessage = await this.callApi('retryFailedMessage', 'messageApi', 'sendMessage', () => this.messageApi.sendMessage(command), command);
|
|
3446
3506
|
// Replace with real message
|
|
3447
3507
|
this.state.deleteMessage(messageId);
|
|
3448
3508
|
this.state.addMessage(sentMessage);
|
|
@@ -3470,7 +3530,7 @@ class AXConversationService {
|
|
|
3470
3530
|
editedAt: new Date(),
|
|
3471
3531
|
});
|
|
3472
3532
|
// Sync with server
|
|
3473
|
-
await this.messageApi.editMessage(messageId, payload);
|
|
3533
|
+
await this.callApi('editMessage', 'messageApi', 'editMessage', () => this.messageApi.editMessage(messageId, payload), messageId, payload);
|
|
3474
3534
|
}
|
|
3475
3535
|
catch (error) {
|
|
3476
3536
|
// Rollback on failure
|
|
@@ -3503,7 +3563,7 @@ class AXConversationService {
|
|
|
3503
3563
|
// Optimistic delete
|
|
3504
3564
|
this.state.deleteMessage(messageId);
|
|
3505
3565
|
// Sync with server
|
|
3506
|
-
await this.messageApi.deleteMessage(messageId, forEveryone);
|
|
3566
|
+
await this.callApi('deleteMessage', 'messageApi', 'deleteMessage', () => this.messageApi.deleteMessage(messageId, forEveryone), messageId, forEveryone);
|
|
3507
3567
|
}
|
|
3508
3568
|
catch (error) {
|
|
3509
3569
|
// Rollback on failure
|
|
@@ -3520,7 +3580,7 @@ class AXConversationService {
|
|
|
3520
3580
|
*/
|
|
3521
3581
|
async reactToMessage(messageId, emoji) {
|
|
3522
3582
|
try {
|
|
3523
|
-
await this.messageApi.addReaction(messageId, emoji);
|
|
3583
|
+
await this.callApi('reactToMessage', 'messageApi', 'addReaction', () => this.messageApi.addReaction(messageId, emoji), messageId, emoji);
|
|
3524
3584
|
// Update will come through subscribeToMessageUpdates
|
|
3525
3585
|
}
|
|
3526
3586
|
catch (error) {
|
|
@@ -3533,41 +3593,104 @@ class AXConversationService {
|
|
|
3533
3593
|
* Returns default reactions on error
|
|
3534
3594
|
*/
|
|
3535
3595
|
async getAvailableReactions() {
|
|
3536
|
-
|
|
3537
|
-
return
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
this.
|
|
3541
|
-
|
|
3542
|
-
|
|
3596
|
+
if (this.availableReactionsCache) {
|
|
3597
|
+
return this.availableReactionsCache;
|
|
3598
|
+
}
|
|
3599
|
+
if (!this.availableReactionsPromise) {
|
|
3600
|
+
this.availableReactionsPromise = this.callApi('getAvailableReactions', 'messageApi', 'getAvailableReactions', () => this.messageApi.getAvailableReactions())
|
|
3601
|
+
.then((reactions) => {
|
|
3602
|
+
this.availableReactionsCache = reactions;
|
|
3603
|
+
return reactions;
|
|
3604
|
+
})
|
|
3605
|
+
.catch((error) => {
|
|
3606
|
+
this.errorHandler.handle(error, 'getAvailableReactions');
|
|
3607
|
+
const fallback = ['👍', '❤️', '😂', '😮', '😢', '🙏'];
|
|
3608
|
+
this.availableReactionsCache = fallback;
|
|
3609
|
+
return fallback;
|
|
3610
|
+
})
|
|
3611
|
+
.finally(() => {
|
|
3612
|
+
this.availableReactionsPromise = null;
|
|
3613
|
+
});
|
|
3543
3614
|
}
|
|
3615
|
+
return this.availableReactionsPromise;
|
|
3544
3616
|
}
|
|
3545
3617
|
/**
|
|
3546
|
-
*
|
|
3547
|
-
* Updates message status locally and syncs with server
|
|
3618
|
+
* Queue a message to be marked as read (batched/debounced API sync).
|
|
3548
3619
|
*/
|
|
3549
|
-
|
|
3620
|
+
queueMessageAsRead(messageId) {
|
|
3550
3621
|
const message = this.state.getMessage(messageId);
|
|
3551
|
-
if (!message || message.status === 'read')
|
|
3622
|
+
if (!message || message.status === 'read') {
|
|
3552
3623
|
return;
|
|
3553
|
-
try {
|
|
3554
|
-
// Optimistic update
|
|
3555
|
-
this.state.updateMessage(messageId, { status: 'read' });
|
|
3556
|
-
// Sync with server
|
|
3557
|
-
await this.messageApi.markAsRead(message.conversationId, [messageId]);
|
|
3558
|
-
// Update conversation unread count
|
|
3559
|
-
const conversation = this.state.getConversation(message.conversationId);
|
|
3560
|
-
if (conversation && conversation.unreadCount > 0) {
|
|
3561
|
-
this.state.updateConversation(message.conversationId, {
|
|
3562
|
-
unreadCount: Math.max(0, conversation.unreadCount - 1),
|
|
3563
|
-
});
|
|
3564
|
-
}
|
|
3565
3624
|
}
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3625
|
+
this.state.updateMessage(messageId, { status: 'read' });
|
|
3626
|
+
let queue = this.readQueue.get(message.conversationId);
|
|
3627
|
+
if (!queue) {
|
|
3628
|
+
queue = new Set();
|
|
3629
|
+
this.readQueue.set(message.conversationId, queue);
|
|
3630
|
+
}
|
|
3631
|
+
queue.add(messageId);
|
|
3632
|
+
const conversation = this.state.getConversation(message.conversationId);
|
|
3633
|
+
if (conversation && conversation.unreadCount > 0) {
|
|
3634
|
+
this.state.updateConversation(message.conversationId, {
|
|
3635
|
+
unreadCount: Math.max(0, conversation.unreadCount - 1),
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
this.scheduleReadFlush();
|
|
3639
|
+
}
|
|
3640
|
+
/**
|
|
3641
|
+
* Mark a single message as read (queues for batched sync).
|
|
3642
|
+
*/
|
|
3643
|
+
async markMessageAsRead(messageId) {
|
|
3644
|
+
this.queueMessageAsRead(messageId);
|
|
3645
|
+
}
|
|
3646
|
+
scheduleReadFlush() {
|
|
3647
|
+
if (this.readFlushHandle) {
|
|
3648
|
+
clearTimeout(this.readFlushHandle);
|
|
3649
|
+
}
|
|
3650
|
+
this.readFlushHandle = setTimeout(() => {
|
|
3651
|
+
this.readFlushHandle = undefined;
|
|
3652
|
+
void this.flushReadQueue();
|
|
3653
|
+
}, this.config.messageReadDebounce);
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Drop queued per-message read receipts without syncing to the API.
|
|
3657
|
+
* Used before conversation-level mark-as-read to avoid duplicate API calls.
|
|
3658
|
+
*/
|
|
3659
|
+
discardReadQueue(conversationId) {
|
|
3660
|
+
if (conversationId) {
|
|
3661
|
+
this.readQueue.delete(conversationId);
|
|
3662
|
+
}
|
|
3663
|
+
else {
|
|
3664
|
+
this.readQueue.clear();
|
|
3665
|
+
}
|
|
3666
|
+
if (this.readFlushHandle) {
|
|
3667
|
+
clearTimeout(this.readFlushHandle);
|
|
3668
|
+
this.readFlushHandle = undefined;
|
|
3669
|
+
}
|
|
3670
|
+
if (this.readQueue.size > 0) {
|
|
3671
|
+
this.scheduleReadFlush();
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
async flushReadQueue() {
|
|
3675
|
+
const snapshot = new Map(this.readQueue);
|
|
3676
|
+
this.readQueue.clear();
|
|
3677
|
+
for (const [conversationId, ids] of snapshot) {
|
|
3678
|
+
const messageIds = [...ids];
|
|
3679
|
+
if (messageIds.length === 0) {
|
|
3680
|
+
continue;
|
|
3681
|
+
}
|
|
3682
|
+
try {
|
|
3683
|
+
await this.callApi('flushReadQueue', 'messageApi', 'markAsRead', () => this.messageApi.markAsRead(conversationId, messageIds), conversationId, messageIds);
|
|
3684
|
+
}
|
|
3685
|
+
catch (error) {
|
|
3686
|
+
for (const id of messageIds) {
|
|
3687
|
+
const msg = this.state.getMessage(id);
|
|
3688
|
+
if (msg?.status === 'read') {
|
|
3689
|
+
this.state.updateMessage(id, { status: 'delivered' });
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
this.errorHandler.handle(error, 'flushReadQueue', { conversationId, count: messageIds.length });
|
|
3693
|
+
}
|
|
3571
3694
|
}
|
|
3572
3695
|
}
|
|
3573
3696
|
/**
|
|
@@ -3575,6 +3698,7 @@ class AXConversationService {
|
|
|
3575
3698
|
* Marks messages locally and syncs with server
|
|
3576
3699
|
*/
|
|
3577
3700
|
async markAsRead(conversationId) {
|
|
3701
|
+
await this.flushReadQueue();
|
|
3578
3702
|
const messages = this.state.getConversationMessages(conversationId);
|
|
3579
3703
|
const currentId = this._currentUser()?.id ?? 'current-user';
|
|
3580
3704
|
const unreadMessageIds = messages.filter((m) => m.status !== 'read' && m.senderId !== currentId).map((m) => m.id);
|
|
@@ -3584,7 +3708,7 @@ class AXConversationService {
|
|
|
3584
3708
|
// Optimistic update
|
|
3585
3709
|
this.state.resetUnreadCount(conversationId);
|
|
3586
3710
|
// Sync with server
|
|
3587
|
-
await this.messageApi.markAsRead(conversationId, unreadMessageIds);
|
|
3711
|
+
await this.callApi('markAsRead', 'messageApi', 'markAsRead', () => this.messageApi.markAsRead(conversationId, unreadMessageIds), conversationId, unreadMessageIds);
|
|
3588
3712
|
}
|
|
3589
3713
|
catch (error) {
|
|
3590
3714
|
// Rollback on failure
|
|
@@ -3601,7 +3725,7 @@ class AXConversationService {
|
|
|
3601
3725
|
*/
|
|
3602
3726
|
async sendTypingIndicator(conversationId) {
|
|
3603
3727
|
try {
|
|
3604
|
-
await this.messageApi.sendTypingIndicator(conversationId);
|
|
3728
|
+
await this.callApi('sendTypingIndicator', 'messageApi', 'sendTypingIndicator', () => this.messageApi.sendTypingIndicator(conversationId), conversationId);
|
|
3605
3729
|
}
|
|
3606
3730
|
catch (error) {
|
|
3607
3731
|
// Log but don't throw - typing indicators are not critical
|
|
@@ -3614,7 +3738,7 @@ class AXConversationService {
|
|
|
3614
3738
|
*/
|
|
3615
3739
|
async searchConversations(query) {
|
|
3616
3740
|
try {
|
|
3617
|
-
return await this.conversationApi.searchConversations(query);
|
|
3741
|
+
return await this.callApi('searchConversations', 'conversationApi', 'searchConversations', () => this.conversationApi.searchConversations(query), query);
|
|
3618
3742
|
}
|
|
3619
3743
|
catch (error) {
|
|
3620
3744
|
this.errorHandler.handle(error, 'searchConversations', { query });
|
|
@@ -3636,6 +3760,7 @@ class AXConversationService {
|
|
|
3636
3760
|
if (isFromOtherUser && !isActiveConversation) {
|
|
3637
3761
|
this.state.incrementUnreadCount(message.conversationId);
|
|
3638
3762
|
}
|
|
3763
|
+
this.syncMessageCountFromRealtime(message, isActiveConversation);
|
|
3639
3764
|
}
|
|
3640
3765
|
/**
|
|
3641
3766
|
* Handle message update
|
|
@@ -3651,18 +3776,24 @@ class AXConversationService {
|
|
|
3651
3776
|
this.state.updateMessage(message.id, message);
|
|
3652
3777
|
}
|
|
3653
3778
|
/**
|
|
3654
|
-
*
|
|
3655
|
-
* Emits events when a message is a reply or forward so components can refresh counts
|
|
3779
|
+
* Bump reply/forward counts locally when realtime events arrive outside the active message list.
|
|
3656
3780
|
*/
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
this._messageCountRefresh$.next({ messageId: message.replyTo.id, type: 'reply' });
|
|
3781
|
+
syncMessageCountFromRealtime(message, isActiveConversation) {
|
|
3782
|
+
if (message.replyTo?.id && !isActiveConversation) {
|
|
3783
|
+
this.bumpMessageCount(message.replyTo.id, 'replyCount');
|
|
3661
3784
|
}
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3785
|
+
if (message.forwardedFrom?.messageId && !isActiveConversation) {
|
|
3786
|
+
this.bumpMessageCount(message.forwardedFrom.messageId, 'forwardCount');
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
bumpMessageCount(messageId, field, delta = 1) {
|
|
3790
|
+
const message = this.state.getMessage(messageId);
|
|
3791
|
+
if (!message) {
|
|
3792
|
+
return;
|
|
3665
3793
|
}
|
|
3794
|
+
this.state.updateMessage(messageId, {
|
|
3795
|
+
[field]: (message[field] ?? 0) + delta,
|
|
3796
|
+
});
|
|
3666
3797
|
}
|
|
3667
3798
|
/**
|
|
3668
3799
|
* Handle message deletion
|
|
@@ -3696,12 +3827,6 @@ class AXConversationService {
|
|
|
3696
3827
|
* Updates conversation metadata including unread count, last message, etc.
|
|
3697
3828
|
*/
|
|
3698
3829
|
handleConversationUpdate(conversation) {
|
|
3699
|
-
const lastMessage = conversation.lastMessage;
|
|
3700
|
-
if (lastMessage &&
|
|
3701
|
-
conversation.id === this._activeConversationId() &&
|
|
3702
|
-
!this.state.getMessage(lastMessage.id)) {
|
|
3703
|
-
this.state.addMessage(lastMessage);
|
|
3704
|
-
}
|
|
3705
3830
|
this.state.setConversation(conversation);
|
|
3706
3831
|
}
|
|
3707
3832
|
/**
|
|
@@ -3733,14 +3858,14 @@ class AXConversationService {
|
|
|
3733
3858
|
if (metadata !== undefined)
|
|
3734
3859
|
apiUpdates.metadata = metadata;
|
|
3735
3860
|
if (Object.keys(apiUpdates).length > 0) {
|
|
3736
|
-
await this.conversationApi.updateConversation(conversationId, apiUpdates);
|
|
3861
|
+
await this.callApi('updateConversation', 'conversationApi', 'updateConversation', () => this.conversationApi.updateConversation(conversationId, apiUpdates), conversationId, apiUpdates);
|
|
3737
3862
|
}
|
|
3738
3863
|
if (archived !== undefined) {
|
|
3739
3864
|
if (archived) {
|
|
3740
|
-
await this.conversationApi.archiveConversation(conversationId);
|
|
3865
|
+
await this.callApi('updateConversation', 'conversationApi', 'archiveConversation', () => this.conversationApi.archiveConversation(conversationId), conversationId);
|
|
3741
3866
|
}
|
|
3742
3867
|
else {
|
|
3743
|
-
await this.conversationApi.unarchiveConversation(conversationId);
|
|
3868
|
+
await this.callApi('updateConversation', 'conversationApi', 'unarchiveConversation', () => this.conversationApi.unarchiveConversation(conversationId), conversationId);
|
|
3744
3869
|
}
|
|
3745
3870
|
}
|
|
3746
3871
|
this.state.updateConversation(conversationId, updates);
|
|
@@ -3757,7 +3882,7 @@ class AXConversationService {
|
|
|
3757
3882
|
*/
|
|
3758
3883
|
async updateConversationSettings(conversationId, settings) {
|
|
3759
3884
|
try {
|
|
3760
|
-
await this.conversationApi.updateConversationSettings(conversationId, settings);
|
|
3885
|
+
await this.callApi('updateConversationSettings', 'conversationApi', 'updateConversationSettings', () => this.conversationApi.updateConversationSettings(conversationId, settings), conversationId, settings);
|
|
3761
3886
|
this.state.updateSettings(conversationId, settings);
|
|
3762
3887
|
}
|
|
3763
3888
|
catch (error) {
|
|
@@ -3772,7 +3897,7 @@ class AXConversationService {
|
|
|
3772
3897
|
*/
|
|
3773
3898
|
async updateConversationTitle(conversationId, title) {
|
|
3774
3899
|
try {
|
|
3775
|
-
await this.conversationApi.updateConversation(conversationId, { title });
|
|
3900
|
+
await this.callApi('updateConversationTitle', 'conversationApi', 'updateConversation', () => this.conversationApi.updateConversation(conversationId, { title }), conversationId, { title });
|
|
3776
3901
|
this.state.updateTitle(conversationId, title);
|
|
3777
3902
|
}
|
|
3778
3903
|
catch (error) {
|
|
@@ -3789,7 +3914,8 @@ class AXConversationService {
|
|
|
3789
3914
|
try {
|
|
3790
3915
|
const current = this.state.getConversation(conversationId);
|
|
3791
3916
|
const mergedMetadata = { ...current?.metadata, ...metadata };
|
|
3792
|
-
|
|
3917
|
+
const metadataUpdate = { metadata: mergedMetadata };
|
|
3918
|
+
await this.callApi('updateConversationMetadata', 'conversationApi', 'updateConversation', () => this.conversationApi.updateConversation(conversationId, metadataUpdate), conversationId, metadataUpdate);
|
|
3793
3919
|
this.state.updateMetadata(conversationId, metadata);
|
|
3794
3920
|
}
|
|
3795
3921
|
catch (error) {
|
|
@@ -3821,7 +3947,7 @@ class AXConversationService {
|
|
|
3821
3947
|
if (creatorId && !scopedParticipantIds.includes(creatorId)) {
|
|
3822
3948
|
scopedParticipantIds.unshift(creatorId);
|
|
3823
3949
|
}
|
|
3824
|
-
const
|
|
3950
|
+
const createData = {
|
|
3825
3951
|
type,
|
|
3826
3952
|
participantIds: scopedParticipantIds,
|
|
3827
3953
|
creatorId,
|
|
@@ -3830,7 +3956,8 @@ class AXConversationService {
|
|
|
3830
3956
|
avatar: isPrivate ? undefined : AXConversationService.normalizeOptionalString(metadata?.['avatar']),
|
|
3831
3957
|
icon: isPrivate ? undefined : AXConversationService.normalizeOptionalString(metadata?.['icon']),
|
|
3832
3958
|
metadata: isPrivate ? undefined : metadata,
|
|
3833
|
-
}
|
|
3959
|
+
};
|
|
3960
|
+
const conversation = await this.callApi('createConversation', 'conversationApi', 'createConversation', () => this.conversationApi.createConversation(createData), createData);
|
|
3834
3961
|
this.state.setConversation(conversation);
|
|
3835
3962
|
return resolveConversationForViewer(conversation, creatorId);
|
|
3836
3963
|
}
|
|
@@ -3847,7 +3974,7 @@ class AXConversationService {
|
|
|
3847
3974
|
*/
|
|
3848
3975
|
async getUsers(filters, pagination = { page: 0, pageSize: 50 }) {
|
|
3849
3976
|
try {
|
|
3850
|
-
return await this.userApi.getUsers(pagination, filters);
|
|
3977
|
+
return await this.callApi('getUsers', 'userApi', 'getUsers', () => this.userApi.getUsers(pagination, filters), pagination, filters);
|
|
3851
3978
|
}
|
|
3852
3979
|
catch (error) {
|
|
3853
3980
|
this.errorHandler.handle(error, 'getUsers', { filters, pagination });
|
|
@@ -3859,8 +3986,15 @@ class AXConversationService {
|
|
|
3859
3986
|
* @param conversationId - Conversation ID
|
|
3860
3987
|
*/
|
|
3861
3988
|
async markConversationAsRead(conversationId) {
|
|
3989
|
+
this.discardReadQueue(conversationId);
|
|
3990
|
+
const currentId = this._currentUser()?.id;
|
|
3991
|
+
for (const message of this.state.getConversationMessages(conversationId)) {
|
|
3992
|
+
if (message.status !== 'read' && message.senderId !== currentId) {
|
|
3993
|
+
this.state.updateMessage(message.id, { status: 'read' });
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3862
3996
|
try {
|
|
3863
|
-
await this.conversationApi.markConversationAsRead(conversationId);
|
|
3997
|
+
await this.callApi('markConversationAsRead', 'conversationApi', 'markConversationAsRead', () => this.conversationApi.markConversationAsRead(conversationId), conversationId);
|
|
3864
3998
|
this.state.resetUnreadCount(conversationId);
|
|
3865
3999
|
}
|
|
3866
4000
|
catch (error) {
|
|
@@ -3887,7 +4021,7 @@ class AXConversationService {
|
|
|
3887
4021
|
}
|
|
3888
4022
|
try {
|
|
3889
4023
|
// Delete from server
|
|
3890
|
-
const result = await this.conversationApi.deleteConversation(conversationId);
|
|
4024
|
+
const result = await this.callApi('deleteConversation', 'conversationApi', 'deleteConversation', () => this.conversationApi.deleteConversation(conversationId), conversationId);
|
|
3891
4025
|
if (!result)
|
|
3892
4026
|
return false;
|
|
3893
4027
|
// Delete from store
|
|
@@ -3910,7 +4044,7 @@ class AXConversationService {
|
|
|
3910
4044
|
*/
|
|
3911
4045
|
async getReplyCount(messageId) {
|
|
3912
4046
|
try {
|
|
3913
|
-
return await this.messageApi.getReplyCount(messageId);
|
|
4047
|
+
return await this.callApi('getReplyCount', 'messageApi', 'getReplyCount', () => this.messageApi.getReplyCount(messageId), messageId);
|
|
3914
4048
|
}
|
|
3915
4049
|
catch (error) {
|
|
3916
4050
|
this.errorHandler.handle(error, 'getReplyCount', { messageId });
|
|
@@ -3924,7 +4058,7 @@ class AXConversationService {
|
|
|
3924
4058
|
*/
|
|
3925
4059
|
async getForwardCount(messageId) {
|
|
3926
4060
|
try {
|
|
3927
|
-
return await this.messageApi.getForwardCount(messageId);
|
|
4061
|
+
return await this.callApi('getForwardCount', 'messageApi', 'getForwardCount', () => this.messageApi.getForwardCount(messageId), messageId);
|
|
3928
4062
|
}
|
|
3929
4063
|
catch (error) {
|
|
3930
4064
|
this.errorHandler.handle(error, 'getForwardCount', { messageId });
|
|
@@ -3938,7 +4072,7 @@ class AXConversationService {
|
|
|
3938
4072
|
async disconnect() {
|
|
3939
4073
|
try {
|
|
3940
4074
|
if (this.realtimeApi) {
|
|
3941
|
-
await this.realtimeApi.disconnect();
|
|
4075
|
+
await this.callApi('disconnect', 'realtimeApi', 'disconnect', () => this.realtimeApi.disconnect());
|
|
3942
4076
|
}
|
|
3943
4077
|
}
|
|
3944
4078
|
catch (error) {
|
|
@@ -3955,7 +4089,7 @@ class AXConversationService {
|
|
|
3955
4089
|
*/
|
|
3956
4090
|
async archiveConversation(conversationId) {
|
|
3957
4091
|
try {
|
|
3958
|
-
await this.conversationApi.archiveConversation(conversationId);
|
|
4092
|
+
await this.callApi('archiveConversation', 'conversationApi', 'archiveConversation', () => this.conversationApi.archiveConversation(conversationId), conversationId);
|
|
3959
4093
|
this.state.updateConversation(conversationId, { archived: true });
|
|
3960
4094
|
}
|
|
3961
4095
|
catch (error) {
|
|
@@ -3969,7 +4103,7 @@ class AXConversationService {
|
|
|
3969
4103
|
*/
|
|
3970
4104
|
async unarchiveConversation(conversationId) {
|
|
3971
4105
|
try {
|
|
3972
|
-
await this.conversationApi.unarchiveConversation(conversationId);
|
|
4106
|
+
await this.callApi('unarchiveConversation', 'conversationApi', 'unarchiveConversation', () => this.conversationApi.unarchiveConversation(conversationId), conversationId);
|
|
3973
4107
|
this.state.updateConversation(conversationId, { archived: false });
|
|
3974
4108
|
}
|
|
3975
4109
|
catch (error) {
|
|
@@ -3983,7 +4117,7 @@ class AXConversationService {
|
|
|
3983
4117
|
*/
|
|
3984
4118
|
async pinConversation(conversationId) {
|
|
3985
4119
|
try {
|
|
3986
|
-
await this.conversationApi.pinConversation(conversationId);
|
|
4120
|
+
await this.callApi('pinConversation', 'conversationApi', 'pinConversation', () => this.conversationApi.pinConversation(conversationId), conversationId);
|
|
3987
4121
|
this.state.updateConversation(conversationId, { pinned: true });
|
|
3988
4122
|
}
|
|
3989
4123
|
catch (error) {
|
|
@@ -3997,7 +4131,7 @@ class AXConversationService {
|
|
|
3997
4131
|
*/
|
|
3998
4132
|
async unpinConversation(conversationId) {
|
|
3999
4133
|
try {
|
|
4000
|
-
await this.conversationApi.unpinConversation(conversationId);
|
|
4134
|
+
await this.callApi('unpinConversation', 'conversationApi', 'unpinConversation', () => this.conversationApi.unpinConversation(conversationId), conversationId);
|
|
4001
4135
|
this.state.updateConversation(conversationId, { pinned: false });
|
|
4002
4136
|
}
|
|
4003
4137
|
catch (error) {
|
|
@@ -4012,7 +4146,7 @@ class AXConversationService {
|
|
|
4012
4146
|
*/
|
|
4013
4147
|
async muteConversation(conversationId, duration) {
|
|
4014
4148
|
try {
|
|
4015
|
-
await this.conversationApi.muteConversation(conversationId, duration);
|
|
4149
|
+
await this.callApi('muteConversation', 'conversationApi', 'muteConversation', () => this.conversationApi.muteConversation(conversationId, duration), conversationId, duration);
|
|
4016
4150
|
const mutedUntil = duration ? new Date(Date.now() + duration) : undefined;
|
|
4017
4151
|
this.state.updateSettings(conversationId, { mutedUntil, notifications: false });
|
|
4018
4152
|
}
|
|
@@ -4027,7 +4161,7 @@ class AXConversationService {
|
|
|
4027
4161
|
*/
|
|
4028
4162
|
async unmuteConversation(conversationId) {
|
|
4029
4163
|
try {
|
|
4030
|
-
await this.conversationApi.unmuteConversation(conversationId);
|
|
4164
|
+
await this.callApi('unmuteConversation', 'conversationApi', 'unmuteConversation', () => this.conversationApi.unmuteConversation(conversationId), conversationId);
|
|
4031
4165
|
this.state.updateSettings(conversationId, { mutedUntil: undefined, notifications: true });
|
|
4032
4166
|
}
|
|
4033
4167
|
catch (error) {
|
|
@@ -4042,7 +4176,7 @@ class AXConversationService {
|
|
|
4042
4176
|
*/
|
|
4043
4177
|
async addParticipants(conversationId, userIds) {
|
|
4044
4178
|
try {
|
|
4045
|
-
const updatedConversation = await this.conversationApi.addParticipants(conversationId, userIds);
|
|
4179
|
+
const updatedConversation = await this.callApi('addParticipants', 'conversationApi', 'addParticipants', () => this.conversationApi.addParticipants(conversationId, userIds), conversationId, userIds);
|
|
4046
4180
|
this.state.setConversation(updatedConversation);
|
|
4047
4181
|
}
|
|
4048
4182
|
catch (error) {
|
|
@@ -4057,7 +4191,7 @@ class AXConversationService {
|
|
|
4057
4191
|
*/
|
|
4058
4192
|
async removeParticipant(conversationId, userId) {
|
|
4059
4193
|
try {
|
|
4060
|
-
const updatedConversation = await this.conversationApi.removeParticipant(conversationId, userId);
|
|
4194
|
+
const updatedConversation = await this.callApi('removeParticipant', 'conversationApi', 'removeParticipant', () => this.conversationApi.removeParticipant(conversationId, userId), conversationId, userId);
|
|
4061
4195
|
this.state.setConversation(updatedConversation);
|
|
4062
4196
|
}
|
|
4063
4197
|
catch (error) {
|
|
@@ -4071,7 +4205,7 @@ class AXConversationService {
|
|
|
4071
4205
|
*/
|
|
4072
4206
|
async leaveConversation(conversationId) {
|
|
4073
4207
|
try {
|
|
4074
|
-
await this.conversationApi.leaveConversation(conversationId);
|
|
4208
|
+
await this.callApi('leaveConversation', 'conversationApi', 'leaveConversation', () => this.conversationApi.leaveConversation(conversationId), conversationId);
|
|
4075
4209
|
this.state.deleteConversation(conversationId);
|
|
4076
4210
|
if (this._activeConversationId() === conversationId) {
|
|
4077
4211
|
this._activeConversationId.set(null);
|
|
@@ -4089,7 +4223,7 @@ class AXConversationService {
|
|
|
4089
4223
|
*/
|
|
4090
4224
|
async saveDraft(conversationId, draft) {
|
|
4091
4225
|
try {
|
|
4092
|
-
await this.conversationApi.saveDraft(conversationId, draft);
|
|
4226
|
+
await this.callApi('saveDraft', 'conversationApi', 'saveDraft', () => this.conversationApi.saveDraft(conversationId, draft), conversationId, draft);
|
|
4093
4227
|
this.state.updateConversation(conversationId, { draft });
|
|
4094
4228
|
}
|
|
4095
4229
|
catch (error) {
|
|
@@ -4103,7 +4237,7 @@ class AXConversationService {
|
|
|
4103
4237
|
*/
|
|
4104
4238
|
async clearDraft(conversationId) {
|
|
4105
4239
|
try {
|
|
4106
|
-
await this.conversationApi.clearDraft(conversationId);
|
|
4240
|
+
await this.callApi('clearDraft', 'conversationApi', 'clearDraft', () => this.conversationApi.clearDraft(conversationId), conversationId);
|
|
4107
4241
|
this.state.updateConversation(conversationId, { draft: undefined });
|
|
4108
4242
|
}
|
|
4109
4243
|
catch (error) {
|
|
@@ -4121,7 +4255,7 @@ class AXConversationService {
|
|
|
4121
4255
|
*/
|
|
4122
4256
|
async pinMessage(conversationId, messageId) {
|
|
4123
4257
|
try {
|
|
4124
|
-
await this.messageApi.pinMessage(conversationId, messageId);
|
|
4258
|
+
await this.callApi('pinMessage', 'messageApi', 'pinMessage', () => this.messageApi.pinMessage(conversationId, messageId), conversationId, messageId);
|
|
4125
4259
|
this.state.updateMessage(messageId, { pinned: true });
|
|
4126
4260
|
}
|
|
4127
4261
|
catch (error) {
|
|
@@ -4136,7 +4270,7 @@ class AXConversationService {
|
|
|
4136
4270
|
*/
|
|
4137
4271
|
async unpinMessage(conversationId, messageId) {
|
|
4138
4272
|
try {
|
|
4139
|
-
await this.messageApi.unpinMessage(conversationId, messageId);
|
|
4273
|
+
await this.callApi('unpinMessage', 'messageApi', 'unpinMessage', () => this.messageApi.unpinMessage(conversationId, messageId), conversationId, messageId);
|
|
4140
4274
|
this.state.updateMessage(messageId, { pinned: false });
|
|
4141
4275
|
}
|
|
4142
4276
|
catch (error) {
|
|
@@ -4152,13 +4286,13 @@ class AXConversationService {
|
|
|
4152
4286
|
*/
|
|
4153
4287
|
async forwardMessage(messageId, conversationIds, caption) {
|
|
4154
4288
|
try {
|
|
4155
|
-
|
|
4289
|
+
const forwardCommand = {
|
|
4156
4290
|
messageId,
|
|
4157
4291
|
conversationIds,
|
|
4158
4292
|
caption,
|
|
4159
|
-
}
|
|
4160
|
-
|
|
4161
|
-
this.
|
|
4293
|
+
};
|
|
4294
|
+
await this.callApi('forwardMessage', 'messageApi', 'forwardMessage', () => this.messageApi.forwardMessage(forwardCommand), forwardCommand);
|
|
4295
|
+
this.bumpMessageCount(messageId, 'forwardCount', conversationIds.length);
|
|
4162
4296
|
}
|
|
4163
4297
|
catch (error) {
|
|
4164
4298
|
this.errorHandler.handle(error, 'forwardMessage', { messageId, conversationIds });
|
|
@@ -4173,7 +4307,9 @@ class AXConversationService {
|
|
|
4173
4307
|
*/
|
|
4174
4308
|
async searchMessages(conversationId, query) {
|
|
4175
4309
|
try {
|
|
4176
|
-
const
|
|
4310
|
+
const searchFilters = { query };
|
|
4311
|
+
const pagination = { page: 0, pageSize: 50 };
|
|
4312
|
+
const result = await this.callApi('searchMessages', 'messageApi', 'searchMessages', () => this.messageApi.searchMessages(conversationId, searchFilters, pagination), conversationId, searchFilters, pagination);
|
|
4177
4313
|
return result.items;
|
|
4178
4314
|
}
|
|
4179
4315
|
catch (error) {
|
|
@@ -4188,7 +4324,8 @@ class AXConversationService {
|
|
|
4188
4324
|
*/
|
|
4189
4325
|
async getMessageReplies(messageId) {
|
|
4190
4326
|
try {
|
|
4191
|
-
const
|
|
4327
|
+
const pagination = { page: 0, pageSize: 50 };
|
|
4328
|
+
const result = await this.callApi('getMessageReplies', 'messageApi', 'getMessageReplies', () => this.messageApi.getMessageReplies(messageId, pagination), messageId, pagination);
|
|
4192
4329
|
return result.items;
|
|
4193
4330
|
}
|
|
4194
4331
|
catch (error) {
|
|
@@ -4204,7 +4341,7 @@ class AXConversationService {
|
|
|
4204
4341
|
*/
|
|
4205
4342
|
async exportMessages(conversationId, format) {
|
|
4206
4343
|
try {
|
|
4207
|
-
return await this.messageApi.exportMessages(conversationId, format);
|
|
4344
|
+
return await this.callApi('exportMessages', 'messageApi', 'exportMessages', () => this.messageApi.exportMessages(conversationId, format), conversationId, format);
|
|
4208
4345
|
}
|
|
4209
4346
|
catch (error) {
|
|
4210
4347
|
this.errorHandler.handle(error, 'exportMessages', { conversationId, format });
|
|
@@ -4220,7 +4357,7 @@ class AXConversationService {
|
|
|
4220
4357
|
*/
|
|
4221
4358
|
async getUserSettings() {
|
|
4222
4359
|
try {
|
|
4223
|
-
return await this.userApi.getUserSettings();
|
|
4360
|
+
return await this.callApi('getUserSettings', 'userApi', 'getUserSettings', () => this.userApi.getUserSettings());
|
|
4224
4361
|
}
|
|
4225
4362
|
catch (error) {
|
|
4226
4363
|
this.errorHandler.handle(error, 'getUserSettings');
|
|
@@ -4233,7 +4370,7 @@ class AXConversationService {
|
|
|
4233
4370
|
*/
|
|
4234
4371
|
async updateUserSettings(settings) {
|
|
4235
4372
|
try {
|
|
4236
|
-
await this.userApi.updateUserSettings(settings);
|
|
4373
|
+
await this.callApi('updateUserSettings', 'userApi', 'updateUserSettings', () => this.userApi.updateUserSettings(settings), settings);
|
|
4237
4374
|
}
|
|
4238
4375
|
catch (error) {
|
|
4239
4376
|
this.errorHandler.handle(error, 'updateUserSettings', { settings });
|
|
@@ -4247,7 +4384,7 @@ class AXConversationService {
|
|
|
4247
4384
|
*/
|
|
4248
4385
|
async blockUser(userId, reason) {
|
|
4249
4386
|
try {
|
|
4250
|
-
await this.userApi.blockUser(userId, reason);
|
|
4387
|
+
await this.callApi('blockUser', 'userApi', 'blockUser', () => this.userApi.blockUser(userId, reason), userId, reason);
|
|
4251
4388
|
}
|
|
4252
4389
|
catch (error) {
|
|
4253
4390
|
this.errorHandler.handle(error, 'blockUser', { userId });
|
|
@@ -4260,7 +4397,7 @@ class AXConversationService {
|
|
|
4260
4397
|
*/
|
|
4261
4398
|
async unblockUser(userId) {
|
|
4262
4399
|
try {
|
|
4263
|
-
await this.userApi.unblockUser(userId);
|
|
4400
|
+
await this.callApi('unblockUser', 'userApi', 'unblockUser', () => this.userApi.unblockUser(userId), userId);
|
|
4264
4401
|
}
|
|
4265
4402
|
catch (error) {
|
|
4266
4403
|
this.errorHandler.handle(error, 'unblockUser', { userId });
|
|
@@ -4273,7 +4410,7 @@ class AXConversationService {
|
|
|
4273
4410
|
*/
|
|
4274
4411
|
async getBlockedUsers() {
|
|
4275
4412
|
try {
|
|
4276
|
-
return await this.userApi.getBlockedUsers();
|
|
4413
|
+
return await this.callApi('getBlockedUsers', 'userApi', 'getBlockedUsers', () => this.userApi.getBlockedUsers());
|
|
4277
4414
|
}
|
|
4278
4415
|
catch (error) {
|
|
4279
4416
|
this.errorHandler.handle(error, 'getBlockedUsers');
|
|
@@ -4287,7 +4424,7 @@ class AXConversationService {
|
|
|
4287
4424
|
*/
|
|
4288
4425
|
async updatePresence(status, statusText) {
|
|
4289
4426
|
try {
|
|
4290
|
-
await this.userApi.updatePresence(status, statusText);
|
|
4427
|
+
await this.callApi('updatePresence', 'userApi', 'updatePresence', () => this.userApi.updatePresence(status, statusText), status, statusText);
|
|
4291
4428
|
}
|
|
4292
4429
|
catch (error) {
|
|
4293
4430
|
this.errorHandler.handle(error, 'updatePresence', { status });
|
|
@@ -4646,7 +4783,6 @@ async function openWithFileType(message, registry, ctx) {
|
|
|
4646
4783
|
class AXConversationMessageListService {
|
|
4647
4784
|
constructor() {
|
|
4648
4785
|
this.conversationService = inject(AXConversationService);
|
|
4649
|
-
this.config = inject(AX_CONVERSATION_CONFIG);
|
|
4650
4786
|
this.activeConversation = this.conversationService.activeConversation;
|
|
4651
4787
|
this.activeMessages = this.conversationService.activeMessages;
|
|
4652
4788
|
this.currentUser = this.conversationService.currentUser;
|
|
@@ -4666,6 +4802,16 @@ class AXConversationMessageListService {
|
|
|
4666
4802
|
...(ngDevMode ? [{ debugName: "scrollRequests" }] : /* istanbul ignore next */ []));
|
|
4667
4803
|
/** Live renderer instances keyed by message id (for message actions). */
|
|
4668
4804
|
this._rendererInstances = new Map();
|
|
4805
|
+
/**
|
|
4806
|
+
* Reply counts merged from API fields and loaded messages.
|
|
4807
|
+
*/
|
|
4808
|
+
this.replyCounts = computed(() => this.buildCountMap('reply'), /* @ts-ignore */
|
|
4809
|
+
...(ngDevMode ? [{ debugName: "replyCounts" }] : /* istanbul ignore next */ []));
|
|
4810
|
+
/**
|
|
4811
|
+
* Forward counts merged from API fields and loaded messages.
|
|
4812
|
+
*/
|
|
4813
|
+
this.forwardCounts = computed(() => this.buildCountMap('forward'), /* @ts-ignore */
|
|
4814
|
+
...(ngDevMode ? [{ debugName: "forwardCounts" }] : /* istanbul ignore next */ []));
|
|
4669
4815
|
/** Message grouped by date */
|
|
4670
4816
|
this.messageGroups = computed(() => {
|
|
4671
4817
|
const messages = this.activeMessages();
|
|
@@ -4701,6 +4847,12 @@ class AXConversationMessageListService {
|
|
|
4701
4847
|
getRendererInstance(messageId) {
|
|
4702
4848
|
return this._rendererInstances.get(messageId);
|
|
4703
4849
|
}
|
|
4850
|
+
getReplyCount(messageId) {
|
|
4851
|
+
return this.replyCounts().get(messageId) ?? 0;
|
|
4852
|
+
}
|
|
4853
|
+
getForwardCount(messageId) {
|
|
4854
|
+
return this.forwardCounts().get(messageId) ?? 0;
|
|
4855
|
+
}
|
|
4704
4856
|
/**
|
|
4705
4857
|
* Load older messages when the user scrolls near the top.
|
|
4706
4858
|
*/
|
|
@@ -4754,6 +4906,27 @@ class AXConversationMessageListService {
|
|
|
4754
4906
|
requestScrollToBottom() {
|
|
4755
4907
|
this.scrollRequests.update((n) => n + 1);
|
|
4756
4908
|
}
|
|
4909
|
+
buildCountMap(kind) {
|
|
4910
|
+
const messages = this.activeMessages();
|
|
4911
|
+
const counts = new Map();
|
|
4912
|
+
for (const message of messages) {
|
|
4913
|
+
const fromApi = kind === 'reply' ? message.replyCount : message.forwardCount;
|
|
4914
|
+
if (fromApi != null) {
|
|
4915
|
+
counts.set(message.id, fromApi);
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
for (const message of messages) {
|
|
4919
|
+
if (kind === 'reply' && message.replyTo?.id) {
|
|
4920
|
+
const targetId = message.replyTo.id;
|
|
4921
|
+
counts.set(targetId, (counts.get(targetId) ?? 0) + 1);
|
|
4922
|
+
}
|
|
4923
|
+
if (kind === 'forward' && message.forwardedFrom?.messageId) {
|
|
4924
|
+
const targetId = message.forwardedFrom.messageId;
|
|
4925
|
+
counts.set(targetId, (counts.get(targetId) ?? 0) + 1);
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
return counts;
|
|
4929
|
+
}
|
|
4757
4930
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4758
4931
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListService }); }
|
|
4759
4932
|
}
|
|
@@ -5577,6 +5750,7 @@ class AXConversationContainerComponent {
|
|
|
5577
5750
|
return classes.join(' ');
|
|
5578
5751
|
}, /* @ts-ignore */
|
|
5579
5752
|
...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
|
|
5753
|
+
void this.conversationService.ensureInitialized();
|
|
5580
5754
|
}
|
|
5581
5755
|
get registry() {
|
|
5582
5756
|
return this.conversationService.registry;
|
|
@@ -5613,7 +5787,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
5613
5787
|
'[class.conversation-container-host]': 'true',
|
|
5614
5788
|
'[tabindex]': '"-1"',
|
|
5615
5789
|
}, template: " <div\n [class]=\"containerClasses()\"\n role=\"main\"\n [attr.aria-label]=\"'@acorex:chat.aria.conversation-interface' | translate | async\"\n >\n <!-- Sidebar -->\n <div\n class=\"conversation-sidebar-container\"\n [axResizable]=\"true\"\n [minWidth]=\"config.minSidebarWidth ?? 250\"\n [maxWidth]=\"config.maxSidebarWidth ?? 500\"\n [width]=\"config.defaultSidebarWidth ?? 300\"\n role=\"navigation\"\n [attr.aria-label]=\"'@acorex:chat.aria.conversation-list' | translate | async\"\n >\n <ng-content select=\"ax-conversation-sidebar, [ax-conversation-sidebar]\"></ng-content>\n </div>\n\n <!-- Main conversation view -->\n <div\n class=\"conversation-main-container\"\n role=\"region\"\n [attr.aria-label]=\"\n activeConversation()\n ? ('@acorex:chat.aria.active-conversation' | translate | async)\n : ('@acorex:chat.status.no-conversation-selected' | translate | async)\n \"\n >\n @if (activeConversation(); as conversation) {\n <div class=\"conversation-view\">\n <!-- Info Bar -->\n <ng-content select=\"ax-conversation-info-bar, [ax-conversation-info-bar]\"></ng-content>\n\n <!-- Messages -->\n <div class=\"conversation-messages\">\n <ng-content select=\"ax-conversation-message-list, [ax-conversation-message-list]\"></ng-content>\n </div>\n\n <!-- Composer -->\n <ng-content select=\"ax-conversation-composer, [ax-conversation-composer]\"></ng-content>\n </div>\n } @else {\n <div class=\"conversation-empty-state\">\n <div class=\"empty-state-content\">\n <ng-content select=\"ax-conversation-empty\">\n <svg class=\"empty-state-icon\" width=\"120\" height=\"120\" viewBox=\"0 0 120 120\" fill=\"none\">\n <circle class=\"text-light\" cx=\"60\" cy=\"60\" r=\"50\" fill=\"currentColor\" />\n <path\n class=\"text-on-surface/55\"\n d=\"M40 50h40M40 70h30\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n <h3 class=\"empty-state-title\">\n {{ '@acorex:chat.empty.no-conversation-selected-title' | translate | async }}\n </h3>\n <p class=\"empty-state-description\">\n {{ '@acorex:chat.empty.select-conversation-description' | translate | async }}\n </p>\n </ng-content>\n </div>\n </div>\n }\n </div>\n </div>\n", styles: ["@layer properties;@layer components{ax-conversation-container{display:block;height:100%;width:100%;--tw-outline-style: none;outline-style:none}ax-conversation-container .conversation-container{display:flex;height:100%;width:100%;overflow:hidden;background-color:rgba(var(--ax-sys-color-lightest-surface))}ax-conversation-container .conversation-container-loading{display:flex;align-items:center;justify-content:center}ax-conversation-container .conversation-sidebar-container{display:none;height:100%;width:calc(var(--spacing, .25rem) * 0);flex-shrink:0;border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface))}ax-conversation-container .conversation-sidebar-container:has(*){display:block;width:var(--ax-conversation-sidebar-width, 320px)}ax-conversation-container .conversation-main-container{display:flex;height:100%;flex:1;flex-direction:column;overflow:hidden;container-type:inline-size;container-name:conversation}ax-conversation-container .conversation-view{display:flex;height:100%;width:100%;flex-direction:column}ax-conversation-container .conversation-messages{flex:1;overflow:hidden}ax-conversation-container .conversation-empty-state{display:flex;height:100%;width:100%;align-items:center;justify-content:center;background-color:rgba(var(--ax-sys-color-lightest-surface))}ax-conversation-container .empty-state-content{max-width:var(--container-md, 28rem);padding:calc(var(--spacing, .25rem) * 8);text-align:center}ax-conversation-container .empty-state-icon{margin-inline:auto;margin-bottom:calc(var(--spacing, .25rem) * 6);opacity:80%}ax-conversation-container .empty-state-title{margin:calc(var(--spacing, .25rem) * 0);margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:var(--text-2xl, 1.5rem);line-height:var(--tw-leading, var(--text-2xl--line-height, calc(2 / 1.5)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-container .empty-state-description{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));opacity:70%}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
|
|
5616
|
-
}], propDecorators: { customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], onDocumentPaste: [{
|
|
5790
|
+
}], ctorParameters: () => [], propDecorators: { customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], onDocumentPaste: [{
|
|
5617
5791
|
type: HostListener,
|
|
5618
5792
|
args: ['document:paste', ['$event']]
|
|
5619
5793
|
}] } });
|
|
@@ -5638,6 +5812,7 @@ class AXConversationContainerDirective {
|
|
|
5638
5812
|
this.loading = this.conversationService.loading;
|
|
5639
5813
|
/** Error state */
|
|
5640
5814
|
this.error = this.conversationService.error;
|
|
5815
|
+
void this.conversationService.ensureInitialized();
|
|
5641
5816
|
}
|
|
5642
5817
|
get registry() {
|
|
5643
5818
|
return this.conversationService.registry;
|
|
@@ -5674,7 +5849,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
5674
5849
|
'[style.outline]': '"none"',
|
|
5675
5850
|
},
|
|
5676
5851
|
}]
|
|
5677
|
-
}], propDecorators: { onDocumentPaste: [{
|
|
5852
|
+
}], ctorParameters: () => [], propDecorators: { onDocumentPaste: [{
|
|
5678
5853
|
type: HostListener,
|
|
5679
5854
|
args: ['document:paste', ['$event']]
|
|
5680
5855
|
}] } });
|
|
@@ -6684,11 +6859,6 @@ class AXConversationMessageListComponent {
|
|
|
6684
6859
|
this.noActiveFallbackComponent = AXConversationMessageListNoActiveDefaultComponent;
|
|
6685
6860
|
// Timeout cleanup to prevent memory leaks
|
|
6686
6861
|
this.timeouts = new Set();
|
|
6687
|
-
// Store reply and forward counts from API
|
|
6688
|
-
this.replyCounts = signal(new Map(), /* @ts-ignore */
|
|
6689
|
-
...(ngDevMode ? [{ debugName: "replyCounts" }] : /* istanbul ignore next */ []));
|
|
6690
|
-
this.forwardCounts = signal(new Map(), /* @ts-ignore */
|
|
6691
|
-
...(ngDevMode ? [{ debugName: "forwardCounts" }] : /* istanbul ignore next */ []));
|
|
6692
6862
|
// Reaction picker state
|
|
6693
6863
|
this.reactionPickerTarget = signal(null, /* @ts-ignore */
|
|
6694
6864
|
...(ngDevMode ? [{ debugName: "reactionPickerTarget" }] : /* istanbul ignore next */ []));
|
|
@@ -6706,9 +6876,10 @@ class AXConversationMessageListComponent {
|
|
|
6706
6876
|
...(ngDevMode ? [{ debugName: "messageListDragging" }] : /* istanbul ignore next */ []));
|
|
6707
6877
|
/** While true, older history is being prepended — do not auto-scroll to the latest message. */
|
|
6708
6878
|
this.restoringScrollAfterPrepend = false;
|
|
6709
|
-
// Available reaction emojis from API
|
|
6879
|
+
// Available reaction emojis from API (loaded lazily when picker opens)
|
|
6710
6880
|
this.availableReactions = signal([], /* @ts-ignore */
|
|
6711
6881
|
...(ngDevMode ? [{ debugName: "availableReactions" }] : /* istanbul ignore next */ []));
|
|
6882
|
+
this.reactionsLoading = false;
|
|
6712
6883
|
/** Message list element reference */
|
|
6713
6884
|
this.messageListRef = viewChild('messageList', /* @ts-ignore */
|
|
6714
6885
|
...(ngDevMode ? [{ debugName: "messageListRef" }] : /* istanbul ignore next */ []));
|
|
@@ -6733,6 +6904,7 @@ class AXConversationMessageListComponent {
|
|
|
6733
6904
|
this.currentUser = this.conversationService.currentUser;
|
|
6734
6905
|
/** Message grouped by date - use service */
|
|
6735
6906
|
this.messageGroups = this.messageListService.messageGroups;
|
|
6907
|
+
this.observedReadMessageIds = new Set();
|
|
6736
6908
|
/** Get first unread message ID */
|
|
6737
6909
|
this.firstUnreadMessageId = computed(() => {
|
|
6738
6910
|
const messages = this.messages();
|
|
@@ -6744,8 +6916,7 @@ class AXConversationMessageListComponent {
|
|
|
6744
6916
|
return firstUnread?.id ?? null;
|
|
6745
6917
|
}, /* @ts-ignore */
|
|
6746
6918
|
...(ngDevMode ? [{ debugName: "firstUnreadMessageId" }] : /* istanbul ignore next */ []));
|
|
6747
|
-
|
|
6748
|
-
this.loadAvailableReactions();
|
|
6919
|
+
void this.conversationService.ensureInitialized();
|
|
6749
6920
|
// Close reaction picker when clicking outside
|
|
6750
6921
|
if (typeof document !== 'undefined') {
|
|
6751
6922
|
document.addEventListener('click', (e) => {
|
|
@@ -6769,6 +6940,7 @@ class AXConversationMessageListComponent {
|
|
|
6769
6940
|
untracked(() => {
|
|
6770
6941
|
if (currentConversationId !== previousConversationId) {
|
|
6771
6942
|
this.contextMenuMessageId.set(null);
|
|
6943
|
+
this.observedReadMessageIds.clear();
|
|
6772
6944
|
this.composerService.onConversationChanged(previousConversationId, currentConversationId);
|
|
6773
6945
|
this.infoBarService.onConversationChanged();
|
|
6774
6946
|
}
|
|
@@ -6784,10 +6956,6 @@ class AXConversationMessageListComponent {
|
|
|
6784
6956
|
previousConversationId = currentConversationId;
|
|
6785
6957
|
});
|
|
6786
6958
|
});
|
|
6787
|
-
// Subscribe to global message count refresh events (works across all conversations)
|
|
6788
|
-
this.conversationService.onMessageCountRefresh.pipe(takeUntilDestroyed()).subscribe(({ messageId, type }) => {
|
|
6789
|
-
this.refreshCount(messageId, type);
|
|
6790
|
-
});
|
|
6791
6959
|
// Auto-scroll only when new messages are appended at the bottom (not when loading older history above).
|
|
6792
6960
|
let previousMessageCount = 0;
|
|
6793
6961
|
let previousLastMessageId = null;
|
|
@@ -6882,16 +7050,21 @@ class AXConversationMessageListComponent {
|
|
|
6882
7050
|
const currentUser = this.currentUser();
|
|
6883
7051
|
if (!this.intersectionObserver || !currentUser)
|
|
6884
7052
|
return;
|
|
6885
|
-
|
|
7053
|
+
const container = this.messagesContainerRef()?.nativeElement;
|
|
7054
|
+
if (!container)
|
|
7055
|
+
return;
|
|
6886
7056
|
const unreadMessages = messages.filter((m) => m.status !== 'read' && m.senderId !== currentUser.id);
|
|
6887
|
-
// Observe each unread message
|
|
6888
7057
|
setTimeout(() => {
|
|
6889
|
-
|
|
6890
|
-
|
|
7058
|
+
for (const msg of unreadMessages) {
|
|
7059
|
+
if (this.observedReadMessageIds.has(msg.id)) {
|
|
7060
|
+
continue;
|
|
7061
|
+
}
|
|
7062
|
+
const element = container.querySelector(`[data-message-id="${msg.id}"]`);
|
|
6891
7063
|
if (element && this.intersectionObserver) {
|
|
6892
7064
|
this.intersectionObserver.observe(element);
|
|
7065
|
+
this.observedReadMessageIds.add(msg.id);
|
|
6893
7066
|
}
|
|
6894
|
-
}
|
|
7067
|
+
}
|
|
6895
7068
|
}, 100);
|
|
6896
7069
|
});
|
|
6897
7070
|
}
|
|
@@ -6902,10 +7075,17 @@ class AXConversationMessageListComponent {
|
|
|
6902
7075
|
const message = this.messages().find((m) => m.id === messageId);
|
|
6903
7076
|
if (!message || message.status === 'read')
|
|
6904
7077
|
return;
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
7078
|
+
if (this.config.markConversationReadOnSelect) {
|
|
7079
|
+
const conversation = this.activeConversation();
|
|
7080
|
+
if (conversation && conversation.unreadCount > 0) {
|
|
7081
|
+
return;
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
// Update message status (batched API sync via conversation service)
|
|
7085
|
+
this.conversationService.queueMessageAsRead(messageId);
|
|
7086
|
+
this.observedReadMessageIds.delete(messageId);
|
|
7087
|
+
const container = this.messagesContainerRef()?.nativeElement;
|
|
7088
|
+
const element = container?.querySelector(`[data-message-id="${messageId}"]`);
|
|
6909
7089
|
if (element && this.intersectionObserver) {
|
|
6910
7090
|
this.intersectionObserver.unobserve(element);
|
|
6911
7091
|
}
|
|
@@ -7083,6 +7263,7 @@ class AXConversationMessageListComponent {
|
|
|
7083
7263
|
this.closeReactionPicker();
|
|
7084
7264
|
return;
|
|
7085
7265
|
}
|
|
7266
|
+
void this.ensureAvailableReactions();
|
|
7086
7267
|
const isRtl = isRtlDocument(this.document);
|
|
7087
7268
|
this.reactionPickerPlacement.set(resolveReactionPickerPlacement(this.isOwnMessage(message), isRtl));
|
|
7088
7269
|
this.reactionPickerElement.set(button);
|
|
@@ -7123,16 +7304,22 @@ class AXConversationMessageListComponent {
|
|
|
7123
7304
|
const messages = this.messages();
|
|
7124
7305
|
return messages.find((m) => m.id === messageId);
|
|
7125
7306
|
}
|
|
7126
|
-
/** Load available reactions
|
|
7127
|
-
async
|
|
7307
|
+
/** Load available reactions when the picker is opened (session-cached in service). */
|
|
7308
|
+
async ensureAvailableReactions() {
|
|
7309
|
+
if (this.availableReactions().length > 0 || this.reactionsLoading) {
|
|
7310
|
+
return;
|
|
7311
|
+
}
|
|
7312
|
+
this.reactionsLoading = true;
|
|
7128
7313
|
try {
|
|
7129
|
-
// Fetch reactions from conversation service
|
|
7130
7314
|
const reactions = await this.conversationService.getAvailableReactions();
|
|
7131
7315
|
this.availableReactions.set(reactions);
|
|
7132
7316
|
}
|
|
7133
7317
|
catch (error) {
|
|
7134
7318
|
console.error('Failed to load available reactions:', error);
|
|
7135
7319
|
}
|
|
7320
|
+
finally {
|
|
7321
|
+
this.reactionsLoading = false;
|
|
7322
|
+
}
|
|
7136
7323
|
}
|
|
7137
7324
|
/** Get message actions from registry */
|
|
7138
7325
|
getMessageActions(message) {
|
|
@@ -7305,55 +7492,11 @@ class AXConversationMessageListComponent {
|
|
|
7305
7492
|
params: { date: dateLabel },
|
|
7306
7493
|
});
|
|
7307
7494
|
}
|
|
7308
|
-
/** Get reply count for a message (fetches from API across all conversations) */
|
|
7309
7495
|
getReplyCount(message) {
|
|
7310
|
-
|
|
7311
|
-
if (!counts.has(message.id)) {
|
|
7312
|
-
// Fetch from API asynchronously
|
|
7313
|
-
this.conversationService.getReplyCount(message.id).then((count) => {
|
|
7314
|
-
const newCounts = new Map(this.replyCounts());
|
|
7315
|
-
newCounts.set(message.id, count);
|
|
7316
|
-
this.replyCounts.set(newCounts);
|
|
7317
|
-
});
|
|
7318
|
-
return 0; // Return 0 while loading
|
|
7319
|
-
}
|
|
7320
|
-
return counts.get(message.id) || 0;
|
|
7496
|
+
return this.messageListService.getReplyCount(message.id);
|
|
7321
7497
|
}
|
|
7322
|
-
/** Get forward count for a message (fetches from API across all conversations) */
|
|
7323
7498
|
getForwardCount(message) {
|
|
7324
|
-
|
|
7325
|
-
if (!counts.has(message.id)) {
|
|
7326
|
-
// Fetch from API asynchronously
|
|
7327
|
-
this.conversationService.getForwardCount(message.id).then((count) => {
|
|
7328
|
-
const newCounts = new Map(this.forwardCounts());
|
|
7329
|
-
newCounts.set(message.id, count);
|
|
7330
|
-
this.forwardCounts.set(newCounts);
|
|
7331
|
-
});
|
|
7332
|
-
return 0; // Return 0 while loading
|
|
7333
|
-
}
|
|
7334
|
-
return counts.get(message.id) || 0;
|
|
7335
|
-
}
|
|
7336
|
-
/**
|
|
7337
|
-
* Refresh count for a specific message from API
|
|
7338
|
-
* This is called when a new reply or forward is detected globally
|
|
7339
|
-
*/
|
|
7340
|
-
refreshCount(messageId, type) {
|
|
7341
|
-
if (type === 'reply') {
|
|
7342
|
-
// Fetch updated reply count from API
|
|
7343
|
-
this.conversationService.getReplyCount(messageId).then((count) => {
|
|
7344
|
-
const newCounts = new Map(this.replyCounts());
|
|
7345
|
-
newCounts.set(messageId, count);
|
|
7346
|
-
this.replyCounts.set(newCounts);
|
|
7347
|
-
});
|
|
7348
|
-
}
|
|
7349
|
-
else {
|
|
7350
|
-
// Fetch updated forward count from API
|
|
7351
|
-
this.conversationService.getForwardCount(messageId).then((count) => {
|
|
7352
|
-
const newCounts = new Map(this.forwardCounts());
|
|
7353
|
-
newCounts.set(messageId, count);
|
|
7354
|
-
this.forwardCounts.set(newCounts);
|
|
7355
|
-
});
|
|
7356
|
-
}
|
|
7499
|
+
return this.messageListService.getForwardCount(message.id);
|
|
7357
7500
|
}
|
|
7358
7501
|
onMessageListDragEnter() {
|
|
7359
7502
|
this.messageListDragging.set(true);
|
|
@@ -7424,14 +7567,14 @@ class AXConversationMessageListComponent {
|
|
|
7424
7567
|
return message.id;
|
|
7425
7568
|
}
|
|
7426
7569
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7427
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationMessageListComponent, isStandalone: true, selector: "ax-conversation-message-list", outputs: { messageAction: "messageAction" }, viewQueries: [{ propertyName: "reactionPopover", first: true, predicate: ["reactionPopover"], descendants: true, isSignal: true }, { propertyName: "messageListRef", first: true, predicate: ["messageList"], descendants: true, isSignal: true }, { propertyName: "messagesContainerRef", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }], ngImport: i0, template: " @if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div\n class=\"list-empty\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\"\n >\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div\n class=\"date-separator\"\n role=\"separator\"\n [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\"\n >\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @if (getReplyCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getReplyCount(message) + ' replies'\"\n >\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getReplyCount(message) }}</span>\n </span>\n }\n @if (getForwardCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getForwardCount(message) + ' forwards'\"\n >\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getForwardCount(message) }}</span>\n </span>\n }\n @if (getReplyCount(message) > 0 || getForwardCount(message) > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <ax-button\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n animate.enter=\"slide-in\"\n animate.leave=\"slide-out\"\n [text]=\"'\u2193'\"\n [look]=\"'solid'\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n ></ax-button>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n } @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n }\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes slideInFromBottom{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-button.scroll-to-bottom:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list ax-button.scroll-to-bottom{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999;display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:rgba(var(--ax-sys-color-primary-surface));font-size:var(--text-2xl, 1.5rem);line-height:var(--tw-leading, var(--text-2xl--line-height, calc(2 / 1.5)));color:rgba(var(--ax-sys-color-on-primary-surface));transition:transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-in{animation:slideInFromBottom var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-out{opacity:0%;transform:translateY(100%);transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "component", type: AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "component", type: AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: AXConversationInfiniteScrollDirective, selector: "[axInfiniteScroll]", inputs: ["threshold", "edge", "scrollDisabled", "cooldownMs"], outputs: ["scrollThreshold"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "fileType", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { kind: "component", type: AXConversationAvatarComponent, selector: "ax-conversation-avatar", inputs: ["kind", "userId", "conversation", "message", "size", "showStatus", "name", "avatar", "icon"] }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXFormatPipe, name: "format" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
7570
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationMessageListComponent, isStandalone: true, selector: "ax-conversation-message-list", outputs: { messageAction: "messageAction" }, viewQueries: [{ propertyName: "reactionPopover", first: true, predicate: ["reactionPopover"], descendants: true, isSignal: true }, { propertyName: "messageListRef", first: true, predicate: ["messageList"], descendants: true, isSignal: true }, { propertyName: "messagesContainerRef", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div class=\"list-empty\" role=\"status\" [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\">\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div class=\"date-separator\" role=\"separator\" [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\">\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @let replyCount = getReplyCount(message);\n @let forwardCount = getForwardCount(message);\n @if (replyCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"replyCount + ' replies'\">\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ replyCount }}</span>\n </span>\n }\n @if (forwardCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"forwardCount + ' forwards'\">\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ forwardCount }}</span>\n </span>\n }\n @if (replyCount > 0 || forwardCount > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <div\n class=\"scroll-to-bottom-wrapper\"\n animate.enter=\"scroll-to-bottom-enter\"\n animate.leave=\"scroll-to-bottom-leave\"\n >\n <ax-fab\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n >\n <ax-icon><i class=\"fa-light fa-arrow-down\" aria-hidden=\"true\"></i></ax-icon>\n </ax-fab>\n </div>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n} @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n}\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes axScrollToBottomEnter{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes axScrollToBottomLeave{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(100%)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-fab.scroll-to-bottom ax-button:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list .scroll-to-bottom-wrapper{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999}ax-conversation-message-list ax-fab.scroll-to-bottom ax-button{display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none}ax-conversation-message-list .scroll-to-bottom-enter{animation:axScrollToBottomEnter var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}ax-conversation-message-list .scroll-to-bottom-leave{animation:axScrollToBottomLeave var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .scroll-to-bottom-enter,ax-conversation-message-list .scroll-to-bottom-leave{animation:none}}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXFabComponent, selector: "ax-fab", inputs: ["label", "popupStyle", "popupPlacement", "size"], outputs: ["popupPlacementChange", "onClick", "onOpened", "onClosed"] }, { kind: "component", type: AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "component", type: AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "component", type: AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: AXConversationInfiniteScrollDirective, selector: "[axInfiniteScroll]", inputs: ["threshold", "edge", "scrollDisabled", "cooldownMs"], outputs: ["scrollThreshold"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "fileType", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { kind: "component", type: AXConversationAvatarComponent, selector: "ax-conversation-avatar", inputs: ["kind", "userId", "conversation", "message", "size", "showStatus", "name", "avatar", "icon"] }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXFormatPipe, name: "format" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
7428
7571
|
}
|
|
7429
7572
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListComponent, decorators: [{
|
|
7430
7573
|
type: Component,
|
|
7431
7574
|
args: [{ selector: 'ax-conversation-message-list', encapsulation: ViewEncapsulation.None, imports: [
|
|
7432
7575
|
CommonModule,
|
|
7433
7576
|
AXBadgeComponent,
|
|
7434
|
-
|
|
7577
|
+
AXFabComponent,
|
|
7435
7578
|
AXContextMenuComponent,
|
|
7436
7579
|
AXDecoratorModule,
|
|
7437
7580
|
AXLabelComponent,
|
|
@@ -7444,7 +7587,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
7444
7587
|
AXUploaderZoneDirective,
|
|
7445
7588
|
AXConversationAvatarComponent,
|
|
7446
7589
|
AXConversationRegistryComponentOutletComponent,
|
|
7447
|
-
], template: " @if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div\n class=\"list-empty\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\"\n >\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div\n class=\"date-separator\"\n role=\"separator\"\n [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\"\n >\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @if (getReplyCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getReplyCount(message) + ' replies'\"\n >\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getReplyCount(message) }}</span>\n </span>\n }\n @if (getForwardCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getForwardCount(message) + ' forwards'\"\n >\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getForwardCount(message) }}</span>\n </span>\n }\n @if (getReplyCount(message) > 0 || getForwardCount(message) > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <ax-button\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n animate.enter=\"slide-in\"\n animate.leave=\"slide-out\"\n [text]=\"'\u2193'\"\n [look]=\"'solid'\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n ></ax-button>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n } @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n }\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes slideInFromBottom{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-button.scroll-to-bottom:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list ax-button.scroll-to-bottom{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999;display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:rgba(var(--ax-sys-color-primary-surface));font-size:var(--text-2xl, 1.5rem);line-height:var(--tw-leading, var(--text-2xl--line-height, calc(2 / 1.5)));color:rgba(var(--ax-sys-color-on-primary-surface));transition:transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-in{animation:slideInFromBottom var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-out{opacity:0%;transform:translateY(100%);transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
|
|
7590
|
+
], template: "@if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div class=\"list-empty\" role=\"status\" [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\">\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div class=\"date-separator\" role=\"separator\" [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\">\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @let replyCount = getReplyCount(message);\n @let forwardCount = getForwardCount(message);\n @if (replyCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"replyCount + ' replies'\">\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ replyCount }}</span>\n </span>\n }\n @if (forwardCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"forwardCount + ' forwards'\">\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ forwardCount }}</span>\n </span>\n }\n @if (replyCount > 0 || forwardCount > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <div\n class=\"scroll-to-bottom-wrapper\"\n animate.enter=\"scroll-to-bottom-enter\"\n animate.leave=\"scroll-to-bottom-leave\"\n >\n <ax-fab\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n >\n <ax-icon><i class=\"fa-light fa-arrow-down\" aria-hidden=\"true\"></i></ax-icon>\n </ax-fab>\n </div>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n} @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n}\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes axScrollToBottomEnter{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes axScrollToBottomLeave{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(100%)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-fab.scroll-to-bottom ax-button:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list .scroll-to-bottom-wrapper{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999}ax-conversation-message-list ax-fab.scroll-to-bottom ax-button{display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none}ax-conversation-message-list .scroll-to-bottom-enter{animation:axScrollToBottomEnter var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}ax-conversation-message-list .scroll-to-bottom-leave{animation:axScrollToBottomLeave var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .scroll-to-bottom-enter,ax-conversation-message-list .scroll-to-bottom-leave{animation:none}}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
|
|
7448
7591
|
}], ctorParameters: () => [], propDecorators: { reactionPopover: [{ type: i0.ViewChild, args: ['reactionPopover', { isSignal: true }] }], messageListRef: [{ type: i0.ViewChild, args: ['messageList', { isSignal: true }] }], messagesContainerRef: [{ type: i0.ViewChild, args: ['messagesContainer', { isSignal: true }] }], messageAction: [{ type: i0.Output, args: ["messageAction"] }] } });
|
|
7449
7592
|
|
|
7450
7593
|
/**
|
|
@@ -7657,6 +7800,7 @@ class AXConversationSidebarService {
|
|
|
7657
7800
|
return result;
|
|
7658
7801
|
}, /* @ts-ignore */
|
|
7659
7802
|
...(ngDevMode ? [{ debugName: "filteredConversations" }] : /* istanbul ignore next */ []));
|
|
7803
|
+
void this.conversationService.ensureInitialized();
|
|
7660
7804
|
}
|
|
7661
7805
|
// Access registry through conversation service
|
|
7662
7806
|
get registry() {
|
|
@@ -7753,7 +7897,7 @@ class AXConversationSidebarService {
|
|
|
7753
7897
|
}
|
|
7754
7898
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationSidebarService, decorators: [{
|
|
7755
7899
|
type: Injectable
|
|
7756
|
-
}] });
|
|
7900
|
+
}], ctorParameters: () => [] });
|
|
7757
7901
|
|
|
7758
7902
|
/**
|
|
7759
7903
|
* Sidebar Component (moved under conversation-view)
|
|
@@ -7904,9 +8048,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
7904
8048
|
|
|
7905
8049
|
class AXConversationNewDialogComponent extends AXBasePageComponent {
|
|
7906
8050
|
constructor() {
|
|
7907
|
-
super(
|
|
8051
|
+
super();
|
|
7908
8052
|
this.toastService = inject(AXToastService);
|
|
7909
8053
|
this.translation = inject(AXTranslationService);
|
|
8054
|
+
this.config = inject(AX_CONVERSATION_CONFIG);
|
|
8055
|
+
this.destroyRef = inject(DestroyRef);
|
|
7910
8056
|
/**
|
|
7911
8057
|
* Injected by {@link AXPopupComponent} when this dialog is opened via {@link AXPopupService}.
|
|
7912
8058
|
* Required for `setTitle` / dynamic header — {@link AXBasePageComponent#setTitle} is otherwise unset.
|
|
@@ -8009,6 +8155,11 @@ class AXConversationNewDialogComponent extends AXBasePageComponent {
|
|
|
8009
8155
|
},
|
|
8010
8156
|
byKey: (key) => Promise.resolve(this.availableUsers().find((u) => u.id === key) ?? null),
|
|
8011
8157
|
});
|
|
8158
|
+
this.destroyRef.onDestroy(() => {
|
|
8159
|
+
if (this.searchDebounceTimer) {
|
|
8160
|
+
clearTimeout(this.searchDebounceTimer);
|
|
8161
|
+
}
|
|
8162
|
+
});
|
|
8012
8163
|
}
|
|
8013
8164
|
onCancel() {
|
|
8014
8165
|
const event = new AXComponentCloseEvent();
|
|
@@ -8029,7 +8180,13 @@ class AXConversationNewDialogComponent extends AXBasePageComponent {
|
|
|
8029
8180
|
}
|
|
8030
8181
|
onSearchChange(value) {
|
|
8031
8182
|
this.searchQuery.set(value ?? '');
|
|
8032
|
-
this.
|
|
8183
|
+
if (this.searchDebounceTimer) {
|
|
8184
|
+
clearTimeout(this.searchDebounceTimer);
|
|
8185
|
+
}
|
|
8186
|
+
this.searchDebounceTimer = setTimeout(() => {
|
|
8187
|
+
this.searchDebounceTimer = undefined;
|
|
8188
|
+
this.usersList()?.refresh(false);
|
|
8189
|
+
}, this.config.debounceSearch ?? 300);
|
|
8033
8190
|
}
|
|
8034
8191
|
isUserSelected(id) {
|
|
8035
8192
|
return this.selectedUserIds().includes(id);
|
|
@@ -8091,7 +8248,7 @@ class AXConversationNewDialogComponent extends AXBasePageComponent {
|
|
|
8091
8248
|
this.creating.set(false);
|
|
8092
8249
|
}
|
|
8093
8250
|
}
|
|
8094
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationNewDialogComponent, deps:
|
|
8251
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationNewDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8095
8252
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationNewDialogComponent, isStandalone: true, selector: "ax-conversation-new-dialog", inputs: { __popup__: { classPropertyName: "__popup__", publicName: "__popup__", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXClosableComponent, useExisting: AXConversationNewDialogComponent }], viewQueries: [{ propertyName: "usersListEmptyTpl", first: true, predicate: ["usersListEmpty"], descendants: true, isSignal: true }, { propertyName: "usersList", first: true, predicate: ["usersList"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: " <div class=\"new-conversation-panel\">\n <div class=\"new-conversation-dialog-content\">\n @if (showUserPickerStep()) {\n <div class=\"form-field form-field--grow\">\n <div class=\"users-list-wrap\">\n <div class=\"users-list-search\">\n <ax-search-box\n [value]=\"searchQuery()\"\n (valueChange)=\"onSearchChange($event)\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.search-users' | translate | async\"\n ></ax-search-box>\n </div>\n <ax-list\n #usersList\n class=\"users-list\"\n [dataSource]=\"usersListDataSource\"\n [multiple]=\"true\"\n [checkbox]=\"false\"\n [disabled]=\"creating()\"\n [valueField]=\"'id'\"\n [textField]=\"'name'\"\n [itemHeight]=\"60\"\n [ngModel]=\"selectedUserIds()\"\n (ngModelChange)=\"onSelectedUsersChange($event ?? [])\"\n [emptyTemplate]=\"usersListEmptyTpl()\"\n [itemTemplate]=\"userItemTpl\"\n ></ax-list>\n </div>\n </div>\n }\n\n @if (showGroupDetailsStep()) {\n <div class=\"group-details-step\">\n <div class=\"form-field\">\n <input\n class=\"title-input\"\n [ngModel]=\"groupTitle()\"\n (ngModelChange)=\"groupTitle.set($event ?? '')\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.group-title-required' | translate | async\"\n />\n </div>\n <div class=\"form-field\">\n <ax-conversation-avatar-picker [(avatarUrl)]=\"groupAvatar\"></ax-conversation-avatar-picker>\n </div>\n </div>\n }\n </div>\n <ax-footer class=\"dialog-footer\">\n <ax-suffix>\n @if (showGroupBackButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.back' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onBackFromGroupDetails()\"\n ></ax-button>\n } @else {\n <ax-button\n [text]=\"'@acorex:chat.actions.cancel' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onCancel()\"\n ></ax-button>\n }\n @if (showNextButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.next' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canGoNextToGroupDetails() || creating()\"\n (onClick)=\"onNextToGroupDetails()\"\n ></ax-button>\n }\n @if (showCreateButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.create' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canCreate() || creating()\"\n (onClick)=\"onCreateConversation()\"\n ></ax-button>\n }\n </ax-suffix>\n </ax-footer>\n </div>\n\n <ng-template #usersListEmpty>\n <div class=\"users-list-empty\">\n @if (searchQuery()) {\n {{ '@acorex:chat.dialog.new-conversation.no-users-search' | translate | async }}\n } @else {\n {{ '@acorex:chat.dialog.new-conversation.no-users-available' | translate | async }}\n }\n </div>\n </ng-template>\n\n <ng-template #userItemTpl let-item>\n @if (userFromItem(item); as user) {\n <div class=\"user-item-content\">\n <input class=\"ax-checkbox\" type=\"checkbox\" [checked]=\"isUserSelected(user.id)\" tabindex=\"0\" />\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"user.id\"\n [size]=\"48\"\n [name]=\"user.name\"\n [avatar]=\"user.avatar\"\n [icon]=\"user.icon\"\n />\n <div class=\"user-item-text\">\n <span class=\"truncated user-name-text\">{{ user.name }}</span>\n @if (user.description) {\n <span class=\"user-description-text truncated\">{{ user.description }}</span>\n }\n </div>\n </div>\n }\n </ng-template>\n", styles: ["@layer properties;@layer components{ax-conversation-new-dialog{display:block}ax-conversation-new-dialog .new-conversation-panel{display:flex;width:100%;flex-direction:column;overflow:hidden}ax-conversation-new-dialog .new-conversation-dialog-content{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}ax-conversation-new-dialog .form-field--grow{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column}ax-conversation-new-dialog .group-details-step{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .form-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-new-dialog .field-label{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface),.6)}ax-conversation-new-dialog .title-input{height:calc(var(--spacing, .25rem) * 9);border-radius:var(--radius-lg, .5rem);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 2.5);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .title-input:focus{outline:2px solid rgba(var(--ax-sys-color-primary-500),.25);outline-offset:0}ax-conversation-new-dialog .users-list-wrap{display:flex;flex-direction:column;overflow:hidden;height:min(50vh,36rem)}ax-conversation-new-dialog .users-list-search{flex-shrink:0;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .users-list{display:block;min-height:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-new-dialog .user-item-content{display:flex;height:100%;min-width:calc(var(--spacing, .25rem) * 0);align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .user-item-text{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * .5)}ax-conversation-new-dialog .user-name-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .user-description-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-new-dialog .users-list-empty{padding:calc(var(--spacing, .25rem) * 4);text-align:center;font-size:.85rem;opacity:70%}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid;--tw-leading: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXListComponent, selector: "ax-list", inputs: ["id", "name", "disabled", "readonly", "valueField", "textField", "textTemplate", "disabledField", "multiple", "selectionMode", "isItemTruncated", "showItemTooltip", "dataSource", "itemHeight", "itemTemplate", "emptyTemplate", "loadingTemplate", "checkbox"], outputs: ["onValueChanged", "disabledChange", "readonlyChange", "onBlur", "onFocus", "onItemClick", "onItemSelected", "onScrolledIndexChanged"] }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "component", type: AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: AXConversationAvatarPickerComponent, selector: "ax-conversation-avatar-picker", inputs: ["showClearButton", "avatarUrl"], outputs: ["avatarUrlChange", "onFileSelected"] }, { kind: "component", type: AXConversationAvatarComponent, selector: "ax-conversation-avatar", inputs: ["kind", "userId", "conversation", "message", "size", "showStatus", "name", "avatar", "icon"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
8096
8253
|
}
|
|
8097
8254
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationNewDialogComponent, decorators: [{
|
|
@@ -8107,7 +8264,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
8107
8264
|
AXConversationAvatarComponent,
|
|
8108
8265
|
AXTranslationModule,
|
|
8109
8266
|
], providers: [{ provide: AXClosableComponent, useExisting: AXConversationNewDialogComponent }], template: " <div class=\"new-conversation-panel\">\n <div class=\"new-conversation-dialog-content\">\n @if (showUserPickerStep()) {\n <div class=\"form-field form-field--grow\">\n <div class=\"users-list-wrap\">\n <div class=\"users-list-search\">\n <ax-search-box\n [value]=\"searchQuery()\"\n (valueChange)=\"onSearchChange($event)\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.search-users' | translate | async\"\n ></ax-search-box>\n </div>\n <ax-list\n #usersList\n class=\"users-list\"\n [dataSource]=\"usersListDataSource\"\n [multiple]=\"true\"\n [checkbox]=\"false\"\n [disabled]=\"creating()\"\n [valueField]=\"'id'\"\n [textField]=\"'name'\"\n [itemHeight]=\"60\"\n [ngModel]=\"selectedUserIds()\"\n (ngModelChange)=\"onSelectedUsersChange($event ?? [])\"\n [emptyTemplate]=\"usersListEmptyTpl()\"\n [itemTemplate]=\"userItemTpl\"\n ></ax-list>\n </div>\n </div>\n }\n\n @if (showGroupDetailsStep()) {\n <div class=\"group-details-step\">\n <div class=\"form-field\">\n <input\n class=\"title-input\"\n [ngModel]=\"groupTitle()\"\n (ngModelChange)=\"groupTitle.set($event ?? '')\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.group-title-required' | translate | async\"\n />\n </div>\n <div class=\"form-field\">\n <ax-conversation-avatar-picker [(avatarUrl)]=\"groupAvatar\"></ax-conversation-avatar-picker>\n </div>\n </div>\n }\n </div>\n <ax-footer class=\"dialog-footer\">\n <ax-suffix>\n @if (showGroupBackButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.back' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onBackFromGroupDetails()\"\n ></ax-button>\n } @else {\n <ax-button\n [text]=\"'@acorex:chat.actions.cancel' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onCancel()\"\n ></ax-button>\n }\n @if (showNextButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.next' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canGoNextToGroupDetails() || creating()\"\n (onClick)=\"onNextToGroupDetails()\"\n ></ax-button>\n }\n @if (showCreateButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.create' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canCreate() || creating()\"\n (onClick)=\"onCreateConversation()\"\n ></ax-button>\n }\n </ax-suffix>\n </ax-footer>\n </div>\n\n <ng-template #usersListEmpty>\n <div class=\"users-list-empty\">\n @if (searchQuery()) {\n {{ '@acorex:chat.dialog.new-conversation.no-users-search' | translate | async }}\n } @else {\n {{ '@acorex:chat.dialog.new-conversation.no-users-available' | translate | async }}\n }\n </div>\n </ng-template>\n\n <ng-template #userItemTpl let-item>\n @if (userFromItem(item); as user) {\n <div class=\"user-item-content\">\n <input class=\"ax-checkbox\" type=\"checkbox\" [checked]=\"isUserSelected(user.id)\" tabindex=\"0\" />\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"user.id\"\n [size]=\"48\"\n [name]=\"user.name\"\n [avatar]=\"user.avatar\"\n [icon]=\"user.icon\"\n />\n <div class=\"user-item-text\">\n <span class=\"truncated user-name-text\">{{ user.name }}</span>\n @if (user.description) {\n <span class=\"user-description-text truncated\">{{ user.description }}</span>\n }\n </div>\n </div>\n }\n </ng-template>\n", styles: ["@layer properties;@layer components{ax-conversation-new-dialog{display:block}ax-conversation-new-dialog .new-conversation-panel{display:flex;width:100%;flex-direction:column;overflow:hidden}ax-conversation-new-dialog .new-conversation-dialog-content{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}ax-conversation-new-dialog .form-field--grow{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column}ax-conversation-new-dialog .group-details-step{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .form-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-new-dialog .field-label{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface),.6)}ax-conversation-new-dialog .title-input{height:calc(var(--spacing, .25rem) * 9);border-radius:var(--radius-lg, .5rem);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 2.5);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .title-input:focus{outline:2px solid rgba(var(--ax-sys-color-primary-500),.25);outline-offset:0}ax-conversation-new-dialog .users-list-wrap{display:flex;flex-direction:column;overflow:hidden;height:min(50vh,36rem)}ax-conversation-new-dialog .users-list-search{flex-shrink:0;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .users-list{display:block;min-height:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-new-dialog .user-item-content{display:flex;height:100%;min-width:calc(var(--spacing, .25rem) * 0);align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .user-item-text{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * .5)}ax-conversation-new-dialog .user-name-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .user-description-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-new-dialog .users-list-empty{padding:calc(var(--spacing, .25rem) * 4);text-align:center;font-size:.85rem;opacity:70%}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid;--tw-leading: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
|
|
8110
|
-
}], propDecorators: { __popup__: [{ type: i0.Input, args: [{ isSignal: true, alias: "__popup__", required: false }] }], usersListEmptyTpl: [{ type: i0.ViewChild, args: ['usersListEmpty', { isSignal: true }] }], usersList: [{ type: i0.ViewChild, args: ['usersList', { isSignal: true }] }] } });
|
|
8267
|
+
}], ctorParameters: () => [], propDecorators: { __popup__: [{ type: i0.Input, args: [{ isSignal: true, alias: "__popup__", required: false }] }], usersListEmptyTpl: [{ type: i0.ViewChild, args: ['usersListEmpty', { isSignal: true }] }], usersList: [{ type: i0.ViewChild, args: ['usersList', { isSignal: true }] }] } });
|
|
8111
8268
|
|
|
8112
8269
|
var newConversationDialog_component = /*#__PURE__*/Object.freeze({
|
|
8113
8270
|
__proto__: null,
|
|
@@ -8773,9 +8930,12 @@ var sharedStorage = /*#__PURE__*/Object.freeze({
|
|
|
8773
8930
|
/**
|
|
8774
8931
|
* Extra seed data so sidebar, message-list, and user-picker pagination can be exercised in the demo.
|
|
8775
8932
|
*/
|
|
8933
|
+
/** Dedicated chat for message-list infinite-scroll / page loading tests. */
|
|
8934
|
+
const AX_MESSAGE_PAGINATION_CONVERSATION_ID = 'conv-pag-msgs';
|
|
8776
8935
|
const AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION = 200;
|
|
8777
|
-
const
|
|
8936
|
+
const AX_CONVERSATION_MESSAGE_PAGINATION_COUNT = 500;
|
|
8778
8937
|
const AX_CONVERSATION_MIN_USERS_FOR_PAGINATION = 50;
|
|
8938
|
+
const AX_PAGINATION_DB_BATCH_SIZE = 50;
|
|
8779
8939
|
/**
|
|
8780
8940
|
* Append demo users when the participant list is too small for the new-conversation picker (page size 10).
|
|
8781
8941
|
*/
|
|
@@ -8807,14 +8967,88 @@ async function ensureUsersPaginationDemoData() {
|
|
|
8807
8967
|
await axConversationIndexedDbStorage.putParticipant(participant);
|
|
8808
8968
|
}
|
|
8809
8969
|
}
|
|
8970
|
+
/**
|
|
8971
|
+
* Create a pinned private chat with {@link AX_CONVERSATION_MESSAGE_PAGINATION_COUNT} text messages.
|
|
8972
|
+
*/
|
|
8973
|
+
async function ensureMessagePaginationConversation(currentUser, peer, now) {
|
|
8974
|
+
const existingCount = axConversationSharedStorage.messagesByConversation.get(AX_MESSAGE_PAGINATION_CONVERSATION_ID)?.length ?? 0;
|
|
8975
|
+
if (existingCount >= AX_CONVERSATION_MESSAGE_PAGINATION_COUNT) {
|
|
8976
|
+
return;
|
|
8977
|
+
}
|
|
8978
|
+
if (!axConversationSharedStorage.conversations.has(AX_MESSAGE_PAGINATION_CONVERSATION_ID)) {
|
|
8979
|
+
const conversation = {
|
|
8980
|
+
id: AX_MESSAGE_PAGINATION_CONVERSATION_ID,
|
|
8981
|
+
type: 'private',
|
|
8982
|
+
title: `Message Pagination (${AX_CONVERSATION_MESSAGE_PAGINATION_COUNT})`,
|
|
8983
|
+
participants: [currentUser, { ...peer, role: 'member' }],
|
|
8984
|
+
lastMessageAt: new Date(now),
|
|
8985
|
+
unreadCount: 0,
|
|
8986
|
+
pinned: true,
|
|
8987
|
+
status: { isTyping: false, typingUsers: [] },
|
|
8988
|
+
settings: { notifications: true, showPreview: true },
|
|
8989
|
+
archived: false,
|
|
8990
|
+
metadata: {
|
|
8991
|
+
profile: {
|
|
8992
|
+
description: 'Mock chat with 500 messages for testing message-list pagination',
|
|
8993
|
+
tags: ['demo', 'pagination', 'messages'],
|
|
8994
|
+
favorite: true,
|
|
8995
|
+
},
|
|
8996
|
+
},
|
|
8997
|
+
createdAt: new Date(now - AX_CONVERSATION_MESSAGE_PAGINATION_COUNT * 60_000),
|
|
8998
|
+
updatedAt: new Date(now),
|
|
8999
|
+
};
|
|
9000
|
+
axConversationSharedStorage.conversations.set(conversation.id, conversation);
|
|
9001
|
+
await axConversationIndexedDbStorage.putConversation(conversation);
|
|
9002
|
+
}
|
|
9003
|
+
const toCreate = AX_CONVERSATION_MESSAGE_PAGINATION_COUNT - existingCount;
|
|
9004
|
+
const pendingDbWrites = [];
|
|
9005
|
+
let lastMessage;
|
|
9006
|
+
for (let i = 0; i < toCreate; i++) {
|
|
9007
|
+
const messageNumber = existingCount + i + 1;
|
|
9008
|
+
const isOwn = messageNumber % 2 === 0;
|
|
9009
|
+
const message = {
|
|
9010
|
+
id: `msg-pag-test-${messageNumber}`,
|
|
9011
|
+
conversationId: AX_MESSAGE_PAGINATION_CONVERSATION_ID,
|
|
9012
|
+
senderId: isOwn ? currentUser.id : peer.id,
|
|
9013
|
+
type: 'text',
|
|
9014
|
+
timestamp: new Date(now - (AX_CONVERSATION_MESSAGE_PAGINATION_COUNT - messageNumber) * 60_000),
|
|
9015
|
+
payload: {
|
|
9016
|
+
type: 'text',
|
|
9017
|
+
text: `Pagination test message #${messageNumber} of ${AX_CONVERSATION_MESSAGE_PAGINATION_COUNT} — scroll up to load older history.`,
|
|
9018
|
+
},
|
|
9019
|
+
status: 'read',
|
|
9020
|
+
reactions: [],
|
|
9021
|
+
metadata: {},
|
|
9022
|
+
};
|
|
9023
|
+
registerChatMessage(message);
|
|
9024
|
+
pendingDbWrites.push(axConversationIndexedDbStorage.putMessage(message));
|
|
9025
|
+
lastMessage = message;
|
|
9026
|
+
if (pendingDbWrites.length >= AX_PAGINATION_DB_BATCH_SIZE) {
|
|
9027
|
+
await Promise.all(pendingDbWrites);
|
|
9028
|
+
pendingDbWrites.length = 0;
|
|
9029
|
+
}
|
|
9030
|
+
}
|
|
9031
|
+
if (pendingDbWrites.length > 0) {
|
|
9032
|
+
await Promise.all(pendingDbWrites);
|
|
9033
|
+
}
|
|
9034
|
+
const conv = axConversationSharedStorage.conversations.get(AX_MESSAGE_PAGINATION_CONVERSATION_ID);
|
|
9035
|
+
if (conv && lastMessage) {
|
|
9036
|
+
conv.lastMessage = lastMessage;
|
|
9037
|
+
conv.lastMessageAt = lastMessage.timestamp;
|
|
9038
|
+
conv.updatedAt = lastMessage.timestamp;
|
|
9039
|
+
axConversationSharedStorage.conversations.set(conv.id, conv);
|
|
9040
|
+
await axConversationIndexedDbStorage.putConversation(conv);
|
|
9041
|
+
}
|
|
9042
|
+
}
|
|
8810
9043
|
/**
|
|
8811
9044
|
* Append demo chats/messages when the DB is too small for default page sizes (30 / 50).
|
|
8812
9045
|
*/
|
|
8813
9046
|
async function ensurePaginationDemoData() {
|
|
8814
9047
|
await ensureUsersPaginationDemoData();
|
|
8815
9048
|
const conversationCount = axConversationSharedStorage.conversations.size;
|
|
8816
|
-
const
|
|
8817
|
-
if (conversationCount >= AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION &&
|
|
9049
|
+
const pagTestMessages = axConversationSharedStorage.messagesByConversation.get(AX_MESSAGE_PAGINATION_CONVERSATION_ID)?.length ?? 0;
|
|
9050
|
+
if (conversationCount >= AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION &&
|
|
9051
|
+
pagTestMessages >= AX_CONVERSATION_MESSAGE_PAGINATION_COUNT) {
|
|
8818
9052
|
return;
|
|
8819
9053
|
}
|
|
8820
9054
|
const participants = Array.from(axConversationSharedStorage.participants.values());
|
|
@@ -8822,16 +9056,19 @@ async function ensurePaginationDemoData() {
|
|
|
8822
9056
|
if (!currentUser || participants.length < 2) {
|
|
8823
9057
|
return;
|
|
8824
9058
|
}
|
|
9059
|
+
const peer = axConversationSharedStorage.participants.get('user-2') ??
|
|
9060
|
+
participants.find((p) => p.id !== currentUser.id) ??
|
|
9061
|
+
participants[1];
|
|
8825
9062
|
const now = Date.now();
|
|
8826
9063
|
if (conversationCount < AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION) {
|
|
8827
9064
|
const toCreate = AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION - conversationCount;
|
|
8828
9065
|
for (let i = 0; i < toCreate; i++) {
|
|
8829
|
-
const
|
|
9066
|
+
const chatPeer = participants[(i % (participants.length - 1)) + 1];
|
|
8830
9067
|
const conversation = {
|
|
8831
9068
|
id: `conv-pag-${i}`,
|
|
8832
9069
|
type: 'private',
|
|
8833
|
-
title: `Demo Chat ${i + 1} — ${
|
|
8834
|
-
participants: [currentUser, { ...
|
|
9070
|
+
title: `Demo Chat ${i + 1} — ${chatPeer.name}`,
|
|
9071
|
+
participants: [currentUser, { ...chatPeer, role: 'member' }],
|
|
8835
9072
|
lastMessageAt: new Date(now - i * 60_000),
|
|
8836
9073
|
unreadCount: 0,
|
|
8837
9074
|
pinned: i === 0,
|
|
@@ -8846,42 +9083,12 @@ async function ensurePaginationDemoData() {
|
|
|
8846
9083
|
await axConversationIndexedDbStorage.putConversation(conversation);
|
|
8847
9084
|
}
|
|
8848
9085
|
}
|
|
8849
|
-
|
|
8850
|
-
const existing = conv1Messages;
|
|
8851
|
-
const toCreate = AX_CONVERSATION_MIN_MESSAGES_CONV_1 - existing;
|
|
8852
|
-
let lastMessage;
|
|
8853
|
-
for (let i = 0; i < toCreate; i++) {
|
|
8854
|
-
const isOwn = i % 2 === 0;
|
|
8855
|
-
const message = {
|
|
8856
|
-
id: `msg-1-pag-${existing + i}`,
|
|
8857
|
-
conversationId: 'conv-1',
|
|
8858
|
-
senderId: isOwn ? 'current-user' : 'user-2',
|
|
8859
|
-
type: 'text',
|
|
8860
|
-
timestamp: new Date(now - (existing + i + 1) * 45_000),
|
|
8861
|
-
payload: {
|
|
8862
|
-
type: 'text',
|
|
8863
|
-
text: `Pagination demo message #${existing + i + 1} — scroll up to load older history.`,
|
|
8864
|
-
},
|
|
8865
|
-
status: 'read',
|
|
8866
|
-
reactions: [],
|
|
8867
|
-
metadata: {},
|
|
8868
|
-
};
|
|
8869
|
-
registerChatMessage(message);
|
|
8870
|
-
await axConversationIndexedDbStorage.putMessage(message);
|
|
8871
|
-
lastMessage = message;
|
|
8872
|
-
}
|
|
8873
|
-
const conv = axConversationSharedStorage.conversations.get('conv-1');
|
|
8874
|
-
if (conv && lastMessage) {
|
|
8875
|
-
conv.lastMessage = lastMessage;
|
|
8876
|
-
conv.lastMessageAt = lastMessage.timestamp;
|
|
8877
|
-
axConversationSharedStorage.conversations.set(conv.id, conv);
|
|
8878
|
-
await axConversationIndexedDbStorage.putConversation(conv);
|
|
8879
|
-
}
|
|
8880
|
-
}
|
|
9086
|
+
await ensureMessagePaginationConversation(currentUser, peer, now);
|
|
8881
9087
|
}
|
|
8882
9088
|
|
|
8883
9089
|
var paginationDemoData = /*#__PURE__*/Object.freeze({
|
|
8884
9090
|
__proto__: null,
|
|
9091
|
+
AX_MESSAGE_PAGINATION_CONVERSATION_ID: AX_MESSAGE_PAGINATION_CONVERSATION_ID,
|
|
8885
9092
|
ensurePaginationDemoData: ensurePaginationDemoData
|
|
8886
9093
|
});
|
|
8887
9094
|
|
|
@@ -10044,7 +10251,7 @@ class AXConversationIndexedDbConversationApi extends AXConversationApi {
|
|
|
10044
10251
|
});
|
|
10045
10252
|
}
|
|
10046
10253
|
async getConversationMedia(conversationId, pagination = { page: 0, pageSize: 30 }) {
|
|
10047
|
-
const media = getConversationMessagesNewestFirst(conversationId).filter((m) => ['image', 'video', 'file', 'audio'].includes(m.type));
|
|
10254
|
+
const media = getConversationMessagesNewestFirst(conversationId).filter((m) => ['image', 'video', 'file', 'audio', 'voice', 'sticker', 'location'].includes(m.type));
|
|
10048
10255
|
const result = paginateChatNewestFirst(media, pagination, 'message');
|
|
10049
10256
|
return { ...result, items: result.items };
|
|
10050
10257
|
}
|
|
@@ -14388,26 +14595,52 @@ function getMessageAudioItems(message) {
|
|
|
14388
14595
|
}
|
|
14389
14596
|
return normalizeAudioPayload(message.payload).audios;
|
|
14390
14597
|
}
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14394
|
-
|
|
14395
|
-
|
|
14396
|
-
|
|
14397
|
-
|
|
14398
|
-
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14598
|
+
const AX_INFO_PANEL_MEDIA_PAGE_SIZE = 50;
|
|
14599
|
+
async function fetchConversationMediaPage(conversationApi, conversationId, page, pageSize = AX_INFO_PANEL_MEDIA_PAGE_SIZE, cursor) {
|
|
14600
|
+
const result = await conversationApi.getConversationMedia(conversationId, {
|
|
14601
|
+
page,
|
|
14602
|
+
pageSize,
|
|
14603
|
+
cursor,
|
|
14604
|
+
});
|
|
14605
|
+
const items = [];
|
|
14606
|
+
const seenIds = new Set();
|
|
14607
|
+
for (const item of result.items) {
|
|
14608
|
+
const message = item;
|
|
14609
|
+
if (message?.id && !seenIds.has(message.id)) {
|
|
14610
|
+
seenIds.add(message.id);
|
|
14611
|
+
items.push(message);
|
|
14612
|
+
}
|
|
14613
|
+
}
|
|
14614
|
+
return {
|
|
14615
|
+
items,
|
|
14616
|
+
hasMore: result.hasMore,
|
|
14617
|
+
nextCursor: result.nextCursor,
|
|
14618
|
+
page,
|
|
14619
|
+
};
|
|
14620
|
+
}
|
|
14621
|
+
/** @deprecated Use {@link fetchConversationMediaPage} — loads only the first page. */
|
|
14622
|
+
async function fetchConversationMediaMessages(conversationApi, conversationId, pageSize) {
|
|
14623
|
+
const page = await fetchConversationMediaPage(conversationApi, conversationId, 0, pageSize);
|
|
14624
|
+
return page.items.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
14625
|
+
}
|
|
14626
|
+
/** Merge media API results with in-memory messages needed for link/sticker/voice/location tabs. */
|
|
14627
|
+
function mergeInfoPanelMessages(mediaMessages, supplemental) {
|
|
14628
|
+
const byId = new Map();
|
|
14629
|
+
for (const message of mediaMessages) {
|
|
14630
|
+
byId.set(message.id, message);
|
|
14631
|
+
}
|
|
14632
|
+
for (const message of supplemental) {
|
|
14633
|
+
if (!byId.has(message.id)) {
|
|
14634
|
+
byId.set(message.id, message);
|
|
14408
14635
|
}
|
|
14409
14636
|
}
|
|
14410
|
-
return
|
|
14637
|
+
return Array.from(byId.values()).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
14638
|
+
}
|
|
14639
|
+
function filterSupplementalInfoPanelMessages(messages) {
|
|
14640
|
+
return messages.filter((message) => message.type === 'voice' ||
|
|
14641
|
+
message.type === 'sticker' ||
|
|
14642
|
+
message.type === 'location' ||
|
|
14643
|
+
(message.type === 'text' && messageContainsLink(message)));
|
|
14411
14644
|
}
|
|
14412
14645
|
|
|
14413
14646
|
/**
|
|
@@ -14433,6 +14666,11 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
|
|
|
14433
14666
|
...(ngDevMode ? [{ debugName: "conversationMessages" }] : /* istanbul ignore next */ []));
|
|
14434
14667
|
this.loadingMessages = signal(false, /* @ts-ignore */
|
|
14435
14668
|
...(ngDevMode ? [{ debugName: "loadingMessages" }] : /* istanbul ignore next */ []));
|
|
14669
|
+
this.loadingMoreMedia = signal(false, /* @ts-ignore */
|
|
14670
|
+
...(ngDevMode ? [{ debugName: "loadingMoreMedia" }] : /* istanbul ignore next */ []));
|
|
14671
|
+
this.mediaHasMore = signal(false, /* @ts-ignore */
|
|
14672
|
+
...(ngDevMode ? [{ debugName: "mediaHasMore" }] : /* istanbul ignore next */ []));
|
|
14673
|
+
this.mediaNextPage = 1;
|
|
14436
14674
|
this.memberSearchQuery = signal('', /* @ts-ignore */
|
|
14437
14675
|
...(ngDevMode ? [{ debugName: "memberSearchQuery" }] : /* istanbul ignore next */ []));
|
|
14438
14676
|
this.archived = false;
|
|
@@ -14566,15 +14804,17 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
|
|
|
14566
14804
|
}
|
|
14567
14805
|
async loadConversationMessages() {
|
|
14568
14806
|
this.loadingMessages.set(true);
|
|
14807
|
+
this.mediaNextPage = 1;
|
|
14808
|
+
this.mediaNextCursor = undefined;
|
|
14809
|
+
this.mediaHasMore.set(false);
|
|
14569
14810
|
try {
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
14573
|
-
|
|
14574
|
-
|
|
14575
|
-
|
|
14576
|
-
|
|
14577
|
-
this.conversationMessages.set(messages);
|
|
14811
|
+
const page = await fetchConversationMediaPage(this.conversationService.conversationApi, this.conversation.id, 0);
|
|
14812
|
+
this.mediaHasMore.set(page.hasMore);
|
|
14813
|
+
this.mediaNextCursor = page.nextCursor;
|
|
14814
|
+
const supplemental = this.conversationService.activeConversationId() === this.conversation.id
|
|
14815
|
+
? filterSupplementalInfoPanelMessages(this.conversationService.activeMessages())
|
|
14816
|
+
: [];
|
|
14817
|
+
this.conversationMessages.set(mergeInfoPanelMessages(page.items, supplemental));
|
|
14578
14818
|
}
|
|
14579
14819
|
catch (error) {
|
|
14580
14820
|
console.error('Failed to load conversation media messages:', error);
|
|
@@ -14583,6 +14823,25 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
|
|
|
14583
14823
|
this.loadingMessages.set(false);
|
|
14584
14824
|
}
|
|
14585
14825
|
}
|
|
14826
|
+
async loadMoreMedia() {
|
|
14827
|
+
if (!this.mediaHasMore() || this.loadingMoreMedia() || this.loadingMessages()) {
|
|
14828
|
+
return;
|
|
14829
|
+
}
|
|
14830
|
+
this.loadingMoreMedia.set(true);
|
|
14831
|
+
try {
|
|
14832
|
+
const page = await fetchConversationMediaPage(this.conversationService.conversationApi, this.conversation.id, this.mediaNextPage, undefined, this.mediaNextCursor);
|
|
14833
|
+
this.mediaHasMore.set(page.hasMore);
|
|
14834
|
+
this.mediaNextCursor = page.nextCursor;
|
|
14835
|
+
this.mediaNextPage += 1;
|
|
14836
|
+
this.conversationMessages.set(mergeInfoPanelMessages(page.items, this.conversationMessages()));
|
|
14837
|
+
}
|
|
14838
|
+
catch (error) {
|
|
14839
|
+
console.error('Failed to load more conversation media:', error);
|
|
14840
|
+
}
|
|
14841
|
+
finally {
|
|
14842
|
+
this.loadingMoreMedia.set(false);
|
|
14843
|
+
}
|
|
14844
|
+
}
|
|
14586
14845
|
isPrivateConversation() {
|
|
14587
14846
|
return this.conversation.type === 'private';
|
|
14588
14847
|
}
|
|
@@ -14630,6 +14889,9 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
|
|
|
14630
14889
|
messages: this.conversationMessages(),
|
|
14631
14890
|
conversation: this.conversation,
|
|
14632
14891
|
conversationService: this.conversationService,
|
|
14892
|
+
hasMoreMedia: this.mediaHasMore(),
|
|
14893
|
+
loadingMoreMedia: this.loadingMoreMedia(),
|
|
14894
|
+
onLoadMoreMedia: () => this.loadMoreMedia(),
|
|
14633
14895
|
onBack: () => this.closeMediaView(),
|
|
14634
14896
|
};
|
|
14635
14897
|
}
|
|
@@ -15086,6 +15348,12 @@ class AXConversationInfoMediaViewComponent {
|
|
|
15086
15348
|
this.conversation = input.required(/* @ts-ignore */
|
|
15087
15349
|
...(ngDevMode ? [{ debugName: "conversation" }] : /* istanbul ignore next */ []));
|
|
15088
15350
|
this.conversationServiceInput = input.required({ ...(ngDevMode ? { debugName: "conversationServiceInput" } : /* istanbul ignore next */ {}), alias: 'conversationService' });
|
|
15351
|
+
this.hasMoreMedia = input(false, /* @ts-ignore */
|
|
15352
|
+
...(ngDevMode ? [{ debugName: "hasMoreMedia" }] : /* istanbul ignore next */ []));
|
|
15353
|
+
this.loadingMoreMedia = input(false, /* @ts-ignore */
|
|
15354
|
+
...(ngDevMode ? [{ debugName: "loadingMoreMedia" }] : /* istanbul ignore next */ []));
|
|
15355
|
+
this.onLoadMoreMedia = input(undefined, /* @ts-ignore */
|
|
15356
|
+
...(ngDevMode ? [{ debugName: "onLoadMoreMedia" }] : /* istanbul ignore next */ []));
|
|
15089
15357
|
/** Callback for lazy outlet wiring (alternative to the `back` output). */
|
|
15090
15358
|
this.onBack = input(undefined, /* @ts-ignore */
|
|
15091
15359
|
...(ngDevMode ? [{ debugName: "onBack" }] : /* istanbul ignore next */ []));
|
|
@@ -15137,21 +15405,27 @@ class AXConversationInfoMediaViewComponent {
|
|
|
15137
15405
|
this.onBack()?.();
|
|
15138
15406
|
this.back.emit();
|
|
15139
15407
|
}
|
|
15408
|
+
onScrollThreshold(edge) {
|
|
15409
|
+
if (edge === 'bottom' && this.hasMoreMedia() && !this.loadingMoreMedia()) {
|
|
15410
|
+
void this.onLoadMoreMedia()?.();
|
|
15411
|
+
}
|
|
15412
|
+
}
|
|
15140
15413
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationInfoMediaViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15141
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationInfoMediaViewComponent, isStandalone: true, selector: "ax-conversation-info-media-view", inputs: { category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: true, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, conversation: { classPropertyName: "conversation", publicName: "conversation", isSignal: true, isRequired: true, transformFunction: null }, conversationServiceInput: { classPropertyName: "conversationServiceInput", publicName: "conversationService", isSignal: true, isRequired: true, transformFunction: null }, onBack: { classPropertyName: "onBack", publicName: "onBack", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, ngImport: i0, template: "<div class=\"tg-media-view\">\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;overflow-y:auto;overscroll-behavior:contain;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXConversationImageAttachmentComponent, selector: "ax-conversation-image-attachment", inputs: ["item", "alt"] }, { kind: "component", type: AXConversationVideoAttachmentComponent, selector: "ax-conversation-video-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "component", type: AXConversationAudioAttachmentComponent, selector: "ax-conversation-audio-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
15414
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationInfoMediaViewComponent, isStandalone: true, selector: "ax-conversation-info-media-view", inputs: { category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: true, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, conversation: { classPropertyName: "conversation", publicName: "conversation", isSignal: true, isRequired: true, transformFunction: null }, conversationServiceInput: { classPropertyName: "conversationServiceInput", publicName: "conversationService", isSignal: true, isRequired: true, transformFunction: null }, hasMoreMedia: { classPropertyName: "hasMoreMedia", publicName: "hasMoreMedia", isSignal: true, isRequired: false, transformFunction: null }, loadingMoreMedia: { classPropertyName: "loadingMoreMedia", publicName: "loadingMoreMedia", isSignal: true, isRequired: false, transformFunction: null }, onLoadMoreMedia: { classPropertyName: "onLoadMoreMedia", publicName: "onLoadMoreMedia", isSignal: true, isRequired: false, transformFunction: null }, onBack: { classPropertyName: "onBack", publicName: "onBack", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, ngImport: i0, template: "<div\n class=\"tg-media-view\"\n axInfiniteScroll\n edge=\"bottom\"\n [threshold]=\"200\"\n [scrollDisabled]=\"loadingMoreMedia() || !hasMoreMedia()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n>\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n @if (loadingMoreMedia()) {\n <div class=\"tg-media-view__loading-more\">\n {{ '@acorex:chat.info-panel.media.loading' | translate | async }}\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;overflow-y:auto;overscroll-behavior:contain;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}ax-conversation-info-media-view .tg-media-view__loading-more{flex-shrink:0;padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "directive", type: AXConversationInfiniteScrollDirective, selector: "[axInfiniteScroll]", inputs: ["threshold", "edge", "scrollDisabled", "cooldownMs"], outputs: ["scrollThreshold"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXConversationImageAttachmentComponent, selector: "ax-conversation-image-attachment", inputs: ["item", "alt"] }, { kind: "component", type: AXConversationVideoAttachmentComponent, selector: "ax-conversation-video-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "component", type: AXConversationAudioAttachmentComponent, selector: "ax-conversation-audio-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
15142
15415
|
}
|
|
15143
15416
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationInfoMediaViewComponent, decorators: [{
|
|
15144
15417
|
type: Component,
|
|
15145
15418
|
args: [{ selector: 'ax-conversation-info-media-view', encapsulation: ViewEncapsulation.None, imports: [
|
|
15146
15419
|
CommonModule,
|
|
15147
15420
|
AXConversationRegistryComponentOutletComponent,
|
|
15421
|
+
AXConversationInfiniteScrollDirective,
|
|
15148
15422
|
AXDecoratorModule,
|
|
15149
15423
|
AXTranslationModule,
|
|
15150
15424
|
AXConversationImageAttachmentComponent,
|
|
15151
15425
|
AXConversationVideoAttachmentComponent,
|
|
15152
15426
|
AXConversationAudioAttachmentComponent,
|
|
15153
|
-
], template: "<div
|
|
15154
|
-
}], propDecorators: { category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], conversation: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversation", required: true }] }], conversationServiceInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversationService", required: true }] }], onBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "onBack", required: false }] }], back: [{ type: i0.Output, args: ["back"] }] } });
|
|
15427
|
+
], template: "<div\n class=\"tg-media-view\"\n axInfiniteScroll\n edge=\"bottom\"\n [threshold]=\"200\"\n [scrollDisabled]=\"loadingMoreMedia() || !hasMoreMedia()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n>\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n @if (loadingMoreMedia()) {\n <div class=\"tg-media-view__loading-more\">\n {{ '@acorex:chat.info-panel.media.loading' | translate | async }}\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;overflow-y:auto;overscroll-behavior:contain;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}ax-conversation-info-media-view .tg-media-view__loading-more{flex-shrink:0;padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
|
|
15428
|
+
}], propDecorators: { category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], conversation: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversation", required: true }] }], conversationServiceInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversationService", required: true }] }], hasMoreMedia: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMoreMedia", required: false }] }], loadingMoreMedia: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingMoreMedia", required: false }] }], onLoadMoreMedia: [{ type: i0.Input, args: [{ isSignal: true, alias: "onLoadMoreMedia", required: false }] }], onBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "onBack", required: false }] }], back: [{ type: i0.Output, args: ["back"] }] } });
|
|
15155
15429
|
|
|
15156
15430
|
var conversationInfoMediaView_component = /*#__PURE__*/Object.freeze({
|
|
15157
15431
|
__proto__: null,
|
|
@@ -15168,6 +15442,7 @@ class AXConversationInfoBarSearchComponent {
|
|
|
15168
15442
|
this.destroyRef = inject(DestroyRef);
|
|
15169
15443
|
this.infoBarService = inject(AXConversationInfoBarService);
|
|
15170
15444
|
this.conversationService = inject(AXConversationService);
|
|
15445
|
+
this.config = inject(AX_CONVERSATION_CONFIG);
|
|
15171
15446
|
/** Conversation input */
|
|
15172
15447
|
this.conversation = input.required(/* @ts-ignore */
|
|
15173
15448
|
...(ngDevMode ? [{ debugName: "conversation" }] : /* istanbul ignore next */ []));
|
|
@@ -15180,49 +15455,53 @@ class AXConversationInfoBarSearchComponent {
|
|
|
15180
15455
|
this.resultsCount = this.infoBarService.searchResultsCount;
|
|
15181
15456
|
/** Current index from service */
|
|
15182
15457
|
this.currentIndex = this.infoBarService.currentSearchIndex;
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
const activeConv = this.conversationService.activeConversation();
|
|
15187
|
-
if (activeConv?.id === this.conversation().id) {
|
|
15188
|
-
return this.conversationService.activeMessages();
|
|
15189
|
-
}
|
|
15190
|
-
return [];
|
|
15191
|
-
}, /* @ts-ignore */
|
|
15192
|
-
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
15193
|
-
effect(() => {
|
|
15194
|
-
const query = this.searchQuery();
|
|
15195
|
-
if (query) {
|
|
15196
|
-
this.performSearch(query);
|
|
15197
|
-
}
|
|
15198
|
-
else {
|
|
15199
|
-
this.infoBarService.setSearchResults(0, 0);
|
|
15200
|
-
}
|
|
15201
|
-
});
|
|
15458
|
+
this.searchResults = signal([], /* @ts-ignore */
|
|
15459
|
+
...(ngDevMode ? [{ debugName: "searchResults" }] : /* istanbul ignore next */ []));
|
|
15460
|
+
this.searchRequestId = 0;
|
|
15202
15461
|
this.destroyRef.onDestroy(() => {
|
|
15203
15462
|
if (this.scrollTimer)
|
|
15204
15463
|
clearTimeout(this.scrollTimer);
|
|
15464
|
+
if (this.searchDebounceTimer)
|
|
15465
|
+
clearTimeout(this.searchDebounceTimer);
|
|
15205
15466
|
});
|
|
15206
15467
|
}
|
|
15207
15468
|
/** Handle search input change */
|
|
15208
15469
|
onSearchChange(value) {
|
|
15209
15470
|
this.infoBarService.setSearchQuery(value);
|
|
15471
|
+
if (this.searchDebounceTimer) {
|
|
15472
|
+
clearTimeout(this.searchDebounceTimer);
|
|
15473
|
+
}
|
|
15474
|
+
const trimmed = value.trim();
|
|
15475
|
+
if (!trimmed) {
|
|
15476
|
+
this.searchResults.set([]);
|
|
15477
|
+
this.infoBarService.setSearchResults(0, 0);
|
|
15478
|
+
return;
|
|
15479
|
+
}
|
|
15480
|
+
this.searchDebounceTimer = setTimeout(() => {
|
|
15481
|
+
this.searchDebounceTimer = undefined;
|
|
15482
|
+
void this.performSearch(trimmed);
|
|
15483
|
+
}, this.config.debounceSearch ?? 300);
|
|
15210
15484
|
}
|
|
15211
|
-
/** Perform search in messages */
|
|
15212
|
-
performSearch(query) {
|
|
15213
|
-
const
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
if (
|
|
15217
|
-
|
|
15218
|
-
|
|
15485
|
+
/** Perform server-side search in messages */
|
|
15486
|
+
async performSearch(query) {
|
|
15487
|
+
const requestId = ++this.searchRequestId;
|
|
15488
|
+
try {
|
|
15489
|
+
const results = await this.conversationService.searchMessages(this.conversation().id, query);
|
|
15490
|
+
if (requestId !== this.searchRequestId) {
|
|
15491
|
+
return;
|
|
15492
|
+
}
|
|
15493
|
+
this.searchResults.set(results);
|
|
15494
|
+
this.infoBarService.setSearchResults(results.length, 0);
|
|
15495
|
+
if (results.length > 0) {
|
|
15496
|
+
this.scrollToResult(results[0].id);
|
|
15497
|
+
}
|
|
15498
|
+
}
|
|
15499
|
+
catch (error) {
|
|
15500
|
+
console.error('Failed to search messages:', error);
|
|
15501
|
+
if (requestId === this.searchRequestId) {
|
|
15502
|
+
this.searchResults.set([]);
|
|
15503
|
+
this.infoBarService.setSearchResults(0, 0);
|
|
15219
15504
|
}
|
|
15220
|
-
return false;
|
|
15221
|
-
});
|
|
15222
|
-
this.infoBarService.setSearchResults(results.length, 0);
|
|
15223
|
-
// Scroll to first result if exists
|
|
15224
|
-
if (results.length > 0) {
|
|
15225
|
-
this.scrollToResult(results[0].id);
|
|
15226
15505
|
}
|
|
15227
15506
|
}
|
|
15228
15507
|
/** Navigate to next result */
|
|
@@ -15237,23 +15516,11 @@ class AXConversationInfoBarSearchComponent {
|
|
|
15237
15516
|
}
|
|
15238
15517
|
/** Scroll to current search result */
|
|
15239
15518
|
scrollToCurrentResult() {
|
|
15240
|
-
const
|
|
15519
|
+
const results = this.searchResults();
|
|
15241
15520
|
const currentIdx = this.currentIndex();
|
|
15242
|
-
const
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
let matchCount = 0;
|
|
15246
|
-
for (const msg of messages) {
|
|
15247
|
-
if (msg.type === 'text') {
|
|
15248
|
-
const text = msg.payload?.text || '';
|
|
15249
|
-
if (text.toLowerCase().includes(lowerQuery)) {
|
|
15250
|
-
if (matchCount === currentIdx) {
|
|
15251
|
-
this.scrollToResult(msg.id);
|
|
15252
|
-
break;
|
|
15253
|
-
}
|
|
15254
|
-
matchCount++;
|
|
15255
|
-
}
|
|
15256
|
-
}
|
|
15521
|
+
const message = results[currentIdx];
|
|
15522
|
+
if (message) {
|
|
15523
|
+
this.scrollToResult(message.id);
|
|
15257
15524
|
}
|
|
15258
15525
|
}
|
|
15259
15526
|
scrollToResult(messageId) {
|
|
@@ -16969,7 +17236,7 @@ function createProviders(options, includeServices) {
|
|
|
16969
17236
|
if (realtimeApi) {
|
|
16970
17237
|
providers.push({ provide: AXConversationRealtimeApi, useClass: realtimeApi });
|
|
16971
17238
|
}
|
|
16972
|
-
providers.push(AXConversationService, AXConversationComposerService, AXConversationInfoBarService, AXConversationMessageListService, AXConversationSidebarService);
|
|
17239
|
+
providers.push(AXConversationService, AXConversationApiLoggerService, AXConversationComposerService, AXConversationInfoBarService, AXConversationMessageListService, AXConversationSidebarService);
|
|
16973
17240
|
}
|
|
16974
17241
|
if (registry) {
|
|
16975
17242
|
providers.push({ provide: AX_CONVERSATION_REGISTRY_CONFIG, useValue: registry });
|
|
@@ -17343,5 +17610,5 @@ function getErrorMessage(code, params) {
|
|
|
17343
17610
|
* Generated bundle index. Do not edit.
|
|
17344
17611
|
*/
|
|
17345
17612
|
|
|
17346
|
-
export { AXConversationAiResponderService, AXConversationApi, AXConversationAudioAttachmentComponent, AXConversationAudioFileTypeProvider, AXConversationAudioPickerComponent, AXConversationAudioRendererComponent, AXConversationBaseRegistry, AXConversationComposerActionRegistry, AXConversationComposerComponent, AXConversationComposerFileTypesProvider, AXConversationComposerPopupComponent, AXConversationComposerService, AXConversationComposerTabRegistry, AXConversationContainerComponent, AXConversationContainerDirective, AXConversationDateUtilsService, AXConversationEmojiTabComponent, AXConversationErrorHandlerService, AXConversationFallbackRendererComponent, AXConversationFileAttachmentComponent, AXConversationFileFileTypeProvider, AXConversationFilePickerComponent, AXConversationFileRendererComponent, AXConversationForwardMessageDialogComponent, AXConversationImageAttachmentComponent, AXConversationImageFileTypeProvider, AXConversationImagePickerComponent, AXConversationImageRendererComponent, AXConversationIndexedDbConversationApi, AXConversationIndexedDbMessageAiApi, AXConversationIndexedDbMessageApi, AXConversationIndexedDbRealtimeApi, AXConversationIndexedDbStorage, AXConversationIndexedDbStores, AXConversationIndexedDbUserApi, AXConversationInfiniteScrollDirective, AXConversationInfoBarActionRegistry, AXConversationInfoBarComponent, AXConversationInfoBarSearchComponent, AXConversationInfoBarService, AXConversationInfoMediaViewComponent, AXConversationInfoPanelComponent, AXConversationItemActionRegistry, AXConversationLocationPickerComponent, AXConversationLocationRendererComponent, AXConversationMediaPlaybackInfoBarBannerComponent, AXConversationMessageActionRegistry, AXConversationMessageApi, AXConversationMessageListComponent, AXConversationMessageListNoActiveDefaultComponent, AXConversationMessageListService, AXConversationMessageRendererCopyHostComponent, AXConversationMessageRendererRegistry, AXConversationMessageRendererStateComponent, AXConversationMessageUtilsService, AXConversationModule, AXConversationNewDialogComponent, AXConversationPickerCaptionComponent, AXConversationPickerEmptyComponent, AXConversationPickerFooterComponent, AXConversationPickerHeaderComponent, AXConversationPickerShellComponent, AXConversationPickerToolbarComponent, AXConversationRealtimeApi, AXConversationRegistryService, AXConversationService, AXConversationSharedStorage, AXConversationSidebarComponent, AXConversationSidebarService, AXConversationStickerRendererComponent, AXConversationStickerTabComponent, AXConversationSystemRendererComponent, AXConversationTabRegistry, AXConversationTextRendererComponent, AXConversationUserApi, AXConversationVideoAttachmentComponent, AXConversationVideoFileTypeProvider, AXConversationVideoPickerComponent, AXConversationVideoRendererComponent, AXConversationVoiceFileTypeProvider, AXConversationVoiceRecorderComponent, AXConversationVoiceRendererComponent, AX_ALL_CONVERSATION_COMPOSER_TABS, AX_ALL_CONVERSATION_MEDIA_PICKERS, AX_CONVERSATION_AI_API_KEY, AX_CONVERSATION_AUDIO_CATALOG, AX_CONVERSATION_AUDIO_PRESENTATION, AX_CONVERSATION_AUDIO_RENDERER, AX_CONVERSATION_COMPOSER_AUDIO_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_FILE_ACTION, AX_CONVERSATION_COMPOSER_IMAGE_ACTION, AX_CONVERSATION_COMPOSER_LOCATION_ACTION, AX_CONVERSATION_COMPOSER_STICKER_TAB, AX_CONVERSATION_COMPOSER_VIDEO_ACTION, AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION, AX_CONVERSATION_CONFIG, AX_CONVERSATION_CONNECTION_ERRORS, AX_CONVERSATION_CONVERSATION_AVATAR_COMPONENT, AX_CONVERSATION_CURSOR_PREFIX, AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS, AX_CONVERSATION_DEFAULT_COMPOSER_TABS, AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS, AX_CONVERSATION_DEFAULT_CONVERSATION_TABS, AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_RENDERERS, AX_CONVERSATION_ERRORS, AX_CONVERSATION_ERROR_HANDLER_CONFIG, AX_CONVERSATION_ERROR_MESSAGES, AX_CONVERSATION_FALLBACK_RENDERER, AX_CONVERSATION_FILE_CATALOG, AX_CONVERSATION_FILE_ERRORS, AX_CONVERSATION_FILE_PRESENTATION, AX_CONVERSATION_FILE_RENDERER, AX_CONVERSATION_FILE_TYPES_READY, AX_CONVERSATION_IMAGE_CATALOG, AX_CONVERSATION_IMAGE_PRESENTATION, AX_CONVERSATION_IMAGE_RENDERER, AX_CONVERSATION_INFO_BAR_ARCHIVE_ACTION, AX_CONVERSATION_INFO_BAR_BLOCK_ACTION, AX_CONVERSATION_INFO_BAR_DELETE_ACTION, AX_CONVERSATION_INFO_BAR_DIVIDER, AX_CONVERSATION_INFO_BAR_MUTE_ACTION, AX_CONVERSATION_INFO_BAR_SEARCH_ACTION, AX_CONVERSATION_ITEM_BLOCK_ACTION, AX_CONVERSATION_ITEM_DELETE_ACTION, AX_CONVERSATION_ITEM_DIVIDER, AX_CONVERSATION_ITEM_MARK_READ_ACTION, AX_CONVERSATION_ITEM_MUTE_ACTION, AX_CONVERSATION_ITEM_PIN_ACTION, AX_CONVERSATION_LOCATION_ERRORS, AX_CONVERSATION_LOCATION_RENDERER, AX_CONVERSATION_MESSAGE_CURSOR_PREFIX, AX_CONVERSATION_MESSAGE_DELETE_ACTION, AX_CONVERSATION_MESSAGE_EDIT_ACTION, AX_CONVERSATION_MESSAGE_ERRORS, AX_CONVERSATION_MESSAGE_FORWARD_ACTION, AX_CONVERSATION_MESSAGE_REPLY_ACTION, AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE, AX_CONVERSATION_REGISTRY_CONFIG, AX_CONVERSATION_STICKER_API_KEY, AX_CONVERSATION_STICKER_RENDERER, AX_CONVERSATION_SYSTEM_RENDERER, AX_CONVERSATION_TAB_ALL, AX_CONVERSATION_TAB_ARCHIVED, AX_CONVERSATION_TAB_BOT, AX_CONVERSATION_TAB_CHANNELS, AX_CONVERSATION_TAB_GROUPS, AX_CONVERSATION_TAB_PRIVATE, AX_CONVERSATION_TAB_UNREAD, AX_CONVERSATION_TEXT_RENDERER, AX_CONVERSATION_URL_ERRORS, AX_CONVERSATION_USER_AVATAR_COMPONENT, AX_CONVERSATION_USER_ERRORS, AX_CONVERSATION_VIDEO_CATALOG, AX_CONVERSATION_VIDEO_PRESENTATION, AX_CONVERSATION_VIDEO_RENDERER, AX_CONVERSATION_VOICE_CATALOG, AX_CONVERSATION_VOICE_PRESENTATION, AX_CONVERSATION_VOICE_RENDERER, AX_DEFAULT_CONVERSATION_CONFIG, abortPickerUploads, applyAudioLocalPreview, applyConversationFilters, applyFileLocalPreview, applyLocalPreview, applyVideoLocalPreview, applyVoiceLocalPreview, audioItemFromUpload, axConversationIndexedDbStorage, axConversationSharedStorage, bindMediaRendererContentState, buildMediaGalleryTiles, canShowRendererContentError, cleanupPickerUploads, conversationAudioUtilities, conversationFileUtilities, conversationImageUtilities, conversationVideoUtilities, conversationVoiceUtilities, copyWithFileTypeFallback, createConversationAudioFileType, createConversationFileFileType, createConversationImageFileType, createConversationVideoFileType, createConversationVoiceFileType, createLocalPreviewUrl, createObjectUrl, createPickerDragHandlers, createResolvedMediaUrlSignal, deleteUploadedPickerMedia, dismissComposerPickerHost, encodeConversationCursor, encodeMessageCursor, ensurePaginationDemoData,
|
|
17613
|
+
export { AXConversationAiResponderService, AXConversationApi, AXConversationApiLoggerService, AXConversationAudioAttachmentComponent, AXConversationAudioFileTypeProvider, AXConversationAudioPickerComponent, AXConversationAudioRendererComponent, AXConversationBaseRegistry, AXConversationComposerActionRegistry, AXConversationComposerComponent, AXConversationComposerFileTypesProvider, AXConversationComposerPopupComponent, AXConversationComposerService, AXConversationComposerTabRegistry, AXConversationContainerComponent, AXConversationContainerDirective, AXConversationDateUtilsService, AXConversationEmojiTabComponent, AXConversationErrorHandlerService, AXConversationFallbackRendererComponent, AXConversationFileAttachmentComponent, AXConversationFileFileTypeProvider, AXConversationFilePickerComponent, AXConversationFileRendererComponent, AXConversationForwardMessageDialogComponent, AXConversationImageAttachmentComponent, AXConversationImageFileTypeProvider, AXConversationImagePickerComponent, AXConversationImageRendererComponent, AXConversationIndexedDbConversationApi, AXConversationIndexedDbMessageAiApi, AXConversationIndexedDbMessageApi, AXConversationIndexedDbRealtimeApi, AXConversationIndexedDbStorage, AXConversationIndexedDbStores, AXConversationIndexedDbUserApi, AXConversationInfiniteScrollDirective, AXConversationInfoBarActionRegistry, AXConversationInfoBarComponent, AXConversationInfoBarSearchComponent, AXConversationInfoBarService, AXConversationInfoMediaViewComponent, AXConversationInfoPanelComponent, AXConversationItemActionRegistry, AXConversationLocationPickerComponent, AXConversationLocationRendererComponent, AXConversationMediaPlaybackInfoBarBannerComponent, AXConversationMessageActionRegistry, AXConversationMessageApi, AXConversationMessageListComponent, AXConversationMessageListNoActiveDefaultComponent, AXConversationMessageListService, AXConversationMessageRendererCopyHostComponent, AXConversationMessageRendererRegistry, AXConversationMessageRendererStateComponent, AXConversationMessageUtilsService, AXConversationModule, AXConversationNewDialogComponent, AXConversationPickerCaptionComponent, AXConversationPickerEmptyComponent, AXConversationPickerFooterComponent, AXConversationPickerHeaderComponent, AXConversationPickerShellComponent, AXConversationPickerToolbarComponent, AXConversationRealtimeApi, AXConversationRegistryService, AXConversationService, AXConversationSharedStorage, AXConversationSidebarComponent, AXConversationSidebarService, AXConversationStickerRendererComponent, AXConversationStickerTabComponent, AXConversationSystemRendererComponent, AXConversationTabRegistry, AXConversationTextRendererComponent, AXConversationUserApi, AXConversationVideoAttachmentComponent, AXConversationVideoFileTypeProvider, AXConversationVideoPickerComponent, AXConversationVideoRendererComponent, AXConversationVoiceFileTypeProvider, AXConversationVoiceRecorderComponent, AXConversationVoiceRendererComponent, AX_ALL_CONVERSATION_COMPOSER_TABS, AX_ALL_CONVERSATION_MEDIA_PICKERS, AX_CONVERSATION_AI_API_KEY, AX_CONVERSATION_AUDIO_CATALOG, AX_CONVERSATION_AUDIO_PRESENTATION, AX_CONVERSATION_AUDIO_RENDERER, AX_CONVERSATION_COMPOSER_AUDIO_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_FILE_ACTION, AX_CONVERSATION_COMPOSER_IMAGE_ACTION, AX_CONVERSATION_COMPOSER_LOCATION_ACTION, AX_CONVERSATION_COMPOSER_STICKER_TAB, AX_CONVERSATION_COMPOSER_VIDEO_ACTION, AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION, AX_CONVERSATION_CONFIG, AX_CONVERSATION_CONNECTION_ERRORS, AX_CONVERSATION_CONVERSATION_AVATAR_COMPONENT, AX_CONVERSATION_CURSOR_PREFIX, AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS, AX_CONVERSATION_DEFAULT_COMPOSER_TABS, AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS, AX_CONVERSATION_DEFAULT_CONVERSATION_TABS, AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_RENDERERS, AX_CONVERSATION_ERRORS, AX_CONVERSATION_ERROR_HANDLER_CONFIG, AX_CONVERSATION_ERROR_MESSAGES, AX_CONVERSATION_FALLBACK_RENDERER, AX_CONVERSATION_FILE_CATALOG, AX_CONVERSATION_FILE_ERRORS, AX_CONVERSATION_FILE_PRESENTATION, AX_CONVERSATION_FILE_RENDERER, AX_CONVERSATION_FILE_TYPES_READY, AX_CONVERSATION_IMAGE_CATALOG, AX_CONVERSATION_IMAGE_PRESENTATION, AX_CONVERSATION_IMAGE_RENDERER, AX_CONVERSATION_INFO_BAR_ARCHIVE_ACTION, AX_CONVERSATION_INFO_BAR_BLOCK_ACTION, AX_CONVERSATION_INFO_BAR_DELETE_ACTION, AX_CONVERSATION_INFO_BAR_DIVIDER, AX_CONVERSATION_INFO_BAR_MUTE_ACTION, AX_CONVERSATION_INFO_BAR_SEARCH_ACTION, AX_CONVERSATION_ITEM_BLOCK_ACTION, AX_CONVERSATION_ITEM_DELETE_ACTION, AX_CONVERSATION_ITEM_DIVIDER, AX_CONVERSATION_ITEM_MARK_READ_ACTION, AX_CONVERSATION_ITEM_MUTE_ACTION, AX_CONVERSATION_ITEM_PIN_ACTION, AX_CONVERSATION_LOCATION_ERRORS, AX_CONVERSATION_LOCATION_RENDERER, AX_CONVERSATION_MESSAGE_CURSOR_PREFIX, AX_CONVERSATION_MESSAGE_DELETE_ACTION, AX_CONVERSATION_MESSAGE_EDIT_ACTION, AX_CONVERSATION_MESSAGE_ERRORS, AX_CONVERSATION_MESSAGE_FORWARD_ACTION, AX_CONVERSATION_MESSAGE_REPLY_ACTION, AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE, AX_CONVERSATION_REGISTRY_CONFIG, AX_CONVERSATION_STICKER_API_KEY, AX_CONVERSATION_STICKER_RENDERER, AX_CONVERSATION_SYSTEM_RENDERER, AX_CONVERSATION_TAB_ALL, AX_CONVERSATION_TAB_ARCHIVED, AX_CONVERSATION_TAB_BOT, AX_CONVERSATION_TAB_CHANNELS, AX_CONVERSATION_TAB_GROUPS, AX_CONVERSATION_TAB_PRIVATE, AX_CONVERSATION_TAB_UNREAD, AX_CONVERSATION_TEXT_RENDERER, AX_CONVERSATION_URL_ERRORS, AX_CONVERSATION_USER_AVATAR_COMPONENT, AX_CONVERSATION_USER_ERRORS, AX_CONVERSATION_VIDEO_CATALOG, AX_CONVERSATION_VIDEO_PRESENTATION, AX_CONVERSATION_VIDEO_RENDERER, AX_CONVERSATION_VOICE_CATALOG, AX_CONVERSATION_VOICE_PRESENTATION, AX_CONVERSATION_VOICE_RENDERER, AX_DEFAULT_CONVERSATION_CONFIG, AX_MESSAGE_PAGINATION_CONVERSATION_ID, abortPickerUploads, applyAudioLocalPreview, applyConversationFilters, applyFileLocalPreview, applyLocalPreview, applyVideoLocalPreview, applyVoiceLocalPreview, audioItemFromUpload, axConversationIndexedDbStorage, axConversationSharedStorage, bindMediaRendererContentState, buildMediaGalleryTiles, canShowRendererContentError, cleanupPickerUploads, conversationAudioUtilities, conversationFileUtilities, conversationImageUtilities, conversationVideoUtilities, conversationVoiceUtilities, copyWithFileTypeFallback, createConversationAudioFileType, createConversationFileFileType, createConversationImageFileType, createConversationVideoFileType, createConversationVoiceFileType, createLocalPreviewUrl, createObjectUrl, createPickerDragHandlers, createResolvedMediaUrlSignal, deleteUploadedPickerMedia, dismissComposerPickerHost, encodeConversationCursor, encodeMessageCursor, ensurePaginationDemoData, fetchConversationMediaMessages, fetchConversationMediaPage, fileItemFromUpload, filterMessagesByMediaCategory, filterSupplementalInfoPanelMessages, formatDuration, formatErrorMessage, formatFileByteSize, formatFileSize, formatMediaDuration, formatPickerValidationMessage, getConversationLastActivity, getConversationMediaCategories, getConversationMessagesNewestFirst, getConversationProfileFields, getErrorMessage, getMessageAudioItems, getMessageVideoItems, getPickerCancelUploadLabel, getPrivatePeerParticipant, getSortedConversationsForInbox, inferFileExtensionHintFromMessage, isAttachmentListCategory, isComposerTabEnabled, isGenericPrivateConversationTitle, isGridMediaCategory, isMediaPickerEnabled, isMessageDeliveryPending, isNonPersistableMediaUrl, isPickerItemReadyToSend, isUploadAborted, limitFilesToCapacity, mediaCopyText, mergeAudioUploadResult, mergeFileUploadResult, mergeInfoPanelMessages, mergeUploadResult, mergeVideoUploadResult, mergeVoiceUploadResult, mergeWithDefaults, messageContainsLink, normalizeAllConversationMessageIndexes, normalizeAudioPayload, normalizeFilePayload, normalizeImagePayload, normalizeMessagePayload, normalizeMessagePayloadAsync, normalizeVideoPayload, notifyMaxFilesCapacityExceeded, notifyPickerValidationErrors, openWithFileType, paginateChatNewestFirst, paginateChatOldestFirst, parseChatCursor, pickDisplayMediaUrl, pickerItemToMediaReference, pickerItemToUploadResult, provideConversation, provideConversationComposerFileTypes, provideConversationFileCatalog, registerChatMessage, reportMediaLoadError, resolveComposerMaxFiles, resolveConversationAvatarDisplay, resolveConversationComposerTabs, resolveConversationForViewer, resolveConversationMediaPickers, resolveConversationMessageFileType, resolveConversationTitleForViewer, resolveGalleryImageUrl, resolveImageDisplayUrl, resolveParticipantProfile, resolvePersistableMediaUrl, resolvePersistedThumbnailUrl, resolvePrivatePeerParticipant, resolvePrivatePeerUserId, resolveUserAvatarDisplay, resolveVideoThumbnailUrl, revokeObjectUrl, revokePickerBlobPreviews, sanitizeInput, seedPickerInitialFiles, seedSharedStorageInitialData, shouldUseUserAvatarForConversation, sortConversationMessageIds, syncPlaybackInfoBarBanner, toUploaderReference as toMediaItemUploaderReference, toUploaderReference$1 as toUploaderReference, unregisterChatMessage, uploadPickerFile, validateConversationId, validateEmail, validateLatitude, validateLongitude, validateMessagePayload, validateMessageText, validateMessageType, validateUrl, validateUserId, validateUserIds, videoItemFromUpload };
|
|
17347
17614
|
//# sourceMappingURL=acorex-components-conversation.mjs.map
|