@acorex/modules 20.6.0-next.8 → 20.6.0-next.9

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.
Files changed (30) hide show
  1. package/conversation/index.d.ts +77 -240
  2. package/fesm2022/acorex-modules-conversation.mjs +193 -1750
  3. package/fesm2022/acorex-modules-conversation.mjs.map +1 -1
  4. package/fesm2022/{acorex-modules-document-management-acorex-modules-document-management-BsST9TMA.mjs → acorex-modules-document-management-acorex-modules-document-management-ZU1fqI_q.mjs} +22 -23
  5. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-ZU1fqI_q.mjs.map +1 -0
  6. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-DqBJrcid.mjs → acorex-modules-document-management-attachment-widget.component-DMCcZ53H.mjs} +2 -2
  7. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-DqBJrcid.mjs.map → acorex-modules-document-management-attachment-widget.component-DMCcZ53H.mjs.map} +1 -1
  8. package/fesm2022/{acorex-modules-document-management-details-view.component-CQgbdaHw.mjs → acorex-modules-document-management-details-view.component-iov9ayCy.mjs} +2 -2
  9. package/fesm2022/{acorex-modules-document-management-details-view.component-CQgbdaHw.mjs.map → acorex-modules-document-management-details-view.component-iov9ayCy.mjs.map} +1 -1
  10. package/fesm2022/{acorex-modules-document-management-document-signature-popup.component-BftZK2ib.mjs → acorex-modules-document-management-document-signature-popup.component-DoeU-5S2.mjs} +2 -2
  11. package/fesm2022/{acorex-modules-document-management-document-signature-popup.component-BftZK2ib.mjs.map → acorex-modules-document-management-document-signature-popup.component-DoeU-5S2.mjs.map} +1 -1
  12. package/fesm2022/{acorex-modules-document-management-drive-choose.component-Ce0JZcwx.mjs → acorex-modules-document-management-drive-choose.component-0UN17SNF.mjs} +2 -2
  13. package/fesm2022/{acorex-modules-document-management-drive-choose.component-Ce0JZcwx.mjs.map → acorex-modules-document-management-drive-choose.component-0UN17SNF.mjs.map} +1 -1
  14. package/fesm2022/{acorex-modules-document-management-drive.component-CNkA0i-_.mjs → acorex-modules-document-management-drive.component-DzikECQB.mjs} +2 -2
  15. package/fesm2022/{acorex-modules-document-management-drive.component-CNkA0i-_.mjs.map → acorex-modules-document-management-drive.component-DzikECQB.mjs.map} +1 -1
  16. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-Dz8hPGMr.mjs → acorex-modules-document-management-large-icons-view.component-B2zaP-ye.mjs} +2 -2
  17. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-Dz8hPGMr.mjs.map → acorex-modules-document-management-large-icons-view.component-B2zaP-ye.mjs.map} +1 -1
  18. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-CM3pqS_m.mjs → acorex-modules-document-management-large-tiles-view.component-h0N92C7a.mjs} +2 -2
  19. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-CM3pqS_m.mjs.map → acorex-modules-document-management-large-tiles-view.component-h0N92C7a.mjs.map} +1 -1
  20. package/fesm2022/{acorex-modules-document-management-list-view.component-BRkcm83Y.mjs → acorex-modules-document-management-list-view.component-Cq-FBKuo.mjs} +2 -2
  21. package/fesm2022/{acorex-modules-document-management-list-view.component-BRkcm83Y.mjs.map → acorex-modules-document-management-list-view.component-Cq-FBKuo.mjs.map} +1 -1
  22. package/fesm2022/{acorex-modules-document-management-permission-definition.provider--qbpKS77.mjs → acorex-modules-document-management-permission-definition.provider-DWID2ajQ.mjs} +2 -2
  23. package/fesm2022/{acorex-modules-document-management-permission-definition.provider--qbpKS77.mjs.map → acorex-modules-document-management-permission-definition.provider-DWID2ajQ.mjs.map} +1 -1
  24. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-C6JV_Dem.mjs → acorex-modules-document-management-small-icons-view.component-Crb7NDuY.mjs} +2 -2
  25. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-C6JV_Dem.mjs.map → acorex-modules-document-management-small-icons-view.component-Crb7NDuY.mjs.map} +1 -1
  26. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-Be37TTaa.mjs → acorex-modules-document-management-small-tiles-view.component-AhGebx78.mjs} +2 -2
  27. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-Be37TTaa.mjs.map → acorex-modules-document-management-small-tiles-view.component-AhGebx78.mjs.map} +1 -1
  28. package/fesm2022/acorex-modules-document-management.mjs +1 -1
  29. package/package.json +10 -10
  30. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-BsST9TMA.mjs.map +0 -1
@@ -1,72 +1,54 @@
1
- import * as i2$2 from '@acorex/components/conversation';
2
- import { AXConversationModule, AXConversationInputComponent } from '@acorex/components/conversation';
1
+ import { AXConversationModule } from '@acorex/components/conversation';
3
2
  import { AXPSessionService, AXPAuthGuard, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex/platform/auth';
4
- import { AXMEntityCrudServiceImpl, entityMasterCrudActions, entityMasterRecordActions, AXPEntityDataSelectorService, AXPEntityDefinitionRegistryService, ensureListActions, actionExists, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
3
+ import { AXMEntityCrudServiceImpl, entityMasterCrudActions, entityMasterRecordActions, ensureListActions, actionExists, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
5
4
  import { AXPRootLayoutComponent } from '@acorex/platform/themes/default';
6
- import * as i1 from '@angular/common';
7
- import { DatePipe, AsyncPipe, CommonModule } from '@angular/common';
5
+ import * as i3 from '@angular/common';
6
+ import { CommonModule, AsyncPipe, DatePipe } from '@angular/common';
8
7
  import * as i0 from '@angular/core';
9
- import { Injectable, NgModule, inject, input, output, signal, effect, computed, ChangeDetectionStrategy, Component, viewChild, HostListener, afterNextRender, DestroyRef, ViewEncapsulation, Injector, importProvidersFrom } from '@angular/core';
10
- import * as i2$1 from '@angular/router';
11
- import { ActivatedRoute, Router, NavigationEnd, RouterModule, ROUTES } from '@angular/router';
12
- import { createAllQueryView, AXPEntityQueryType, AXPFileStorageService, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, AXP_MENU_PROVIDER } from '@acorex/platform/common';
13
- import * as i1$3 from '@acorex/platform/layout/widget-core';
8
+ import { Injectable, NgModule, inject, signal, computed, effect, ChangeDetectionStrategy, Component, input, viewChild, DestroyRef, ViewEncapsulation, output, Injector, importProvidersFrom } from '@angular/core';
9
+ import { Router, ActivatedRoute, RouterModule, ROUTES } from '@angular/router';
10
+ import { createAllQueryView, AXPEntityQueryType, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, AXP_MENU_PROVIDER } from '@acorex/platform/common';
11
+ import * as i1$2 from '@acorex/platform/layout/widget-core';
14
12
  import { AXPWidgetsCatalog, AXPValueWidgetComponent, AXPWidgetGroupEnum, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
15
- import { AXPUserAvatarComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutToolbarComponent, AXPStateMessageComponent, AXP_TASK_BADGE_PROVIDERS } from '@acorex/platform/layout/components';
16
- import * as i2$3 from '@acorex/platform/workflow';
13
+ import { AXPThemeLayoutBlockComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutHeaderComponent, AXPUserAvatarComponent, AXP_TASK_BADGE_PROVIDERS } from '@acorex/platform/layout/components';
14
+ import * as i2$1 from '@acorex/platform/workflow';
17
15
  import { AXPWorkflowAction, AXPWorkflowModule } from '@acorex/platform/workflow';
18
16
  import { AXMNotificationConnectorService, AXP_NOTIFICATION_DEFINITION_PROVIDER } from '@acorex/modules/notification-management';
19
17
  import { AXMUsersEntityService } from '@acorex/modules/security-management';
20
- import { Subject, filter, startWith, debounceTime } from 'rxjs';
21
- import * as i3 from '@acorex/components/avatar';
22
- import { AXAvatarModule } from '@acorex/components/avatar';
23
- import * as i2 from '@acorex/components/badge';
24
- import { AXBadgeModule } from '@acorex/components/badge';
25
- import * as i5$1 from '@acorex/components/button';
26
- import { AXButtonModule } from '@acorex/components/button';
27
- import * as i4$2 from '@acorex/components/comment';
28
- import { AXCommentModule } from '@acorex/components/comment';
29
- import * as i5 from '@acorex/components/decorators';
18
+ import { Subject, filter } from 'rxjs';
19
+ import * as i5 from '@acorex/components/button';
20
+ import { AXButtonComponent, AXButtonModule } from '@acorex/components/button';
21
+ import { AXConversationService, AXConversationContainerDirective, AXSidebarComponent, AXInfoBarComponent, AXMessageListComponent, AXComposerComponent, provideConversation, AX_CONVERSATION_ITEM_MUTE_ACTION, AX_CONVERSATION_ITEM_PIN_ACTION, AX_CONVERSATION_ITEM_MARK_READ_ACTION, AX_CONVERSATION_ITEM_DIVIDER, AX_CONVERSATION_ITEM_DELETE_ACTION, AX_CONVERSATION_ITEM_BLOCK_ACTION, AX_CONVERSATION_INFO_BAR_SEARCH_ACTION, AX_CONVERSATION_INFO_BAR_INFO_ACTION, AX_CONVERSATION_INFO_BAR_MUTE_ACTION, AX_CONVERSATION_INFO_BAR_DIVIDER, AX_CONVERSATION_INFO_BAR_DELETE_ACTION, AX_CONVERSATION_INFO_BAR_BLOCK_ACTION, AX_CONVERSATION_TAB_ALL, AX_CONVERSATION_TAB_PRIVATE, AX_CONVERSATION_TAB_GROUPS, AX_CONVERSATION_TAB_CHANNELS, AX_CONVERSATION_TAB_UNREAD, AX_CONVERSATION_TAB_ARCHIVED, AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_STICKER_TAB, AX_CONVERSATION_COMPOSER_EMOJI_ACTION, AX_CONVERSATION_COMPOSER_IMAGE_ACTION, AX_CONVERSATION_COMPOSER_VIDEO_ACTION, AX_CONVERSATION_COMPOSER_FILE_ACTION, AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION, AX_CONVERSATION_COMPOSER_AUDIO_ACTION, AX_CONVERSATION_COMPOSER_CONTACT_ACTION, AX_CONVERSATION_COMPOSER_LOCATION_ACTION, AX_CONVERSATION_MESSAGE_REPLY_ACTION, AX_CONVERSATION_MESSAGE_FORWARD_ACTION, AX_CONVERSATION_MESSAGE_EDIT_ACTION, AX_CONVERSATION_MESSAGE_DELETE_ACTION, AX_CONVERSATION_TEXT_RENDERER, AX_CONVERSATION_IMAGE_RENDERER, AX_CONVERSATION_VIDEO_RENDERER, AX_CONVERSATION_AUDIO_RENDERER, AX_CONVERSATION_VOICE_RENDERER, AX_CONVERSATION_FILE_RENDERER, AX_CONVERSATION_LOCATION_RENDERER, AX_CONVERSATION_STICKER_RENDERER, AX_CONVERSATION_CONTACT_RENDERER, AXIndexedDBRealtimeApi, AXIndexedDBMessageAIApi, AXIndexedDBConversationApi, AXIndexedDBUserApi } from '@acorex/components/conversation2';
22
+ import * as i1 from '@acorex/components/decorators';
30
23
  import { AXDecoratorModule } from '@acorex/components/decorators';
24
+ import * as i4$1 from '@acorex/core/translation';
25
+ import { AXTranslationModule, AXTranslationService } from '@acorex/core/translation';
26
+ import { AXPPageLayoutComponent } from '@acorex/platform/layout/views';
27
+ import * as i1$1 from '@acorex/components/wysiwyg';
28
+ import { AXWysiwygModule } from '@acorex/components/wysiwyg';
31
29
  import { AXDialogService } from '@acorex/components/dialog';
32
- import * as i7$1 from '@acorex/components/dropdown';
30
+ import { AXToastService } from '@acorex/components/toast';
31
+ import { trigger, transition, style, animate } from '@angular/animations';
32
+ import { DomSanitizer } from '@angular/platform-browser';
33
+ import { AXAvatarModule } from '@acorex/components/avatar';
34
+ import * as i4 from '@acorex/components/comment';
35
+ import { AXCommentModule } from '@acorex/components/comment';
36
+ import * as i7 from '@acorex/components/dropdown';
33
37
  import { AXDropdownModule } from '@acorex/components/dropdown';
34
- import * as i9$1 from '@acorex/components/dropdown-button';
38
+ import * as i9 from '@acorex/components/dropdown-button';
35
39
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
36
40
  import { AXFormModule } from '@acorex/components/form';
37
41
  import { AXImageModule } from '@acorex/components/image';
38
- import { AXLabelModule } from '@acorex/components/label';
39
- import * as i3$1 from '@acorex/components/loading';
42
+ import * as i6 from '@acorex/components/loading';
40
43
  import { AXLoadingModule } from '@acorex/components/loading';
41
- import { AXContextMenuComponent } from '@acorex/components/menu';
42
- import * as i6 from '@acorex/components/search-box';
43
- import { AXSearchBoxModule } from '@acorex/components/search-box';
44
- import { AXSelectBoxModule } from '@acorex/components/select-box';
45
- import * as i9 from '@acorex/components/skeleton';
44
+ import * as i2 from '@acorex/components/skeleton';
46
45
  import { AXSkeletonModule } from '@acorex/components/skeleton';
47
- import * as i4$1 from '@acorex/components/sliding-item';
48
- import { AXSlidingItemModule } from '@acorex/components/sliding-item';
49
- import * as i7 from '@acorex/components/tabs';
50
- import { AXTabsModule } from '@acorex/components/tabs';
51
- import { AXTextBoxModule } from '@acorex/components/text-box';
52
46
  import { AXToolBarModule } from '@acorex/components/toolbar';
53
- import * as i1$2 from '@acorex/components/wysiwyg';
54
- import { AXWysiwygModule } from '@acorex/components/wysiwyg';
55
47
  import * as i10 from '@acorex/core/format';
56
48
  import { AXFormatModule } from '@acorex/core/format';
57
- import * as i4 from '@acorex/core/translation';
58
- import { AXTranslationModule, AXTranslationService } from '@acorex/core/translation';
59
- import { AXUnsubscriber } from '@acorex/core/utils';
60
- import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
61
- import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageLayoutBase } from '@acorex/platform/layout/views';
62
- import { trigger, transition, style, animate } from '@angular/animations';
63
- import * as i1$1 from '@angular/forms';
64
- import { FormsModule } from '@angular/forms';
65
- import { AXDomChangeDirective } from '@acorex/cdk/dom';
66
- import { AXToastService } from '@acorex/components/toast';
67
- import { AXFileService } from '@acorex/core/file';
68
- import { DomSanitizer } from '@angular/platform-browser';
69
49
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
50
+ import * as i8 from '@angular/forms';
51
+ import { FormsModule } from '@angular/forms';
70
52
  import { AXBasePageComponent } from '@acorex/components/page';
71
53
  import { AXPopupService } from '@acorex/components/popup';
72
54
 
@@ -1487,1719 +1469,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1487
1469
  }]
1488
1470
  }] });
1489
1471
 
1490
- class AXMChatItemComponent {
1472
+ class AXMChatComponent {
1491
1473
  constructor() {
1492
- // Input
1493
- this.data = input.required(...(ngDevMode ? [{ debugName: "data" }] : []));
1494
- this.typing = input(null, ...(ngDevMode ? [{ debugName: "typing" }] : []));
1495
- this.lastMessageReaction = input(null, ...(ngDevMode ? [{ debugName: "lastMessageReaction" }] : []));
1496
- // Output
1497
- this.pressChatItem = output();
1498
- // Services
1499
- this.sessionService = inject(AXPSessionService);
1500
- this.datePipe = inject(DatePipe);
1501
- this.fileStorage = inject(AXPFileStorageService);
1502
- this.chatName = signal({
1503
- fullName: 'Loading...',
1504
- id: '',
1505
- isPrivate: false,
1506
- }, ...(ngDevMode ? [{ debugName: "chatName" }] : []));
1507
- this.#chatInfoEffect = effect(async () => {
1508
- const { members, title, id } = this.data();
1509
- if (members.length > 1) {
1510
- this.chatName.set({
1511
- fullName: title || 'Group Chat',
1512
- id,
1513
- isPrivate: false,
1514
- });
1515
- return;
1516
- }
1517
- const member = members[0];
1518
- if (!member) {
1519
- this.chatName.set({ fullName: 'Unknown User', id: '', isPrivate: true });
1520
- return;
1521
- }
1522
- this.chatName.set({
1523
- fullName: member.fullName || 'Unknown User',
1524
- id: member.id,
1525
- isPrivate: true,
1526
- });
1527
- }, ...(ngDevMode ? [{ debugName: "#chatInfoEffect" }] : []));
1528
- // Group avatar preview handling
1529
- this.groupAvatarUrl = signal(null, ...(ngDevMode ? [{ debugName: "groupAvatarUrl" }] : []));
1530
- this.groupBlobUrl = signal(null, ...(ngDevMode ? [{ debugName: "groupBlobUrl" }] : []));
1531
- this.isGroupAvatarLoading = signal(true, ...(ngDevMode ? [{ debugName: "isGroupAvatarLoading" }] : []));
1532
- this.#groupAvatarEffect = effect(async () => {
1533
- const room = this.data();
1534
- // Only for group chats
1535
- if (!room || (room.members?.length ?? 0) <= 1) {
1536
- this.groupAvatarUrl.set(null);
1537
- this.isGroupAvatarLoading.set(false);
1538
- return;
1539
- }
1540
- const avatar = room.avatar;
1541
- if (!avatar) {
1542
- this.groupAvatarUrl.set(null);
1543
- this.isGroupAvatarLoading.set(false);
1544
- return;
1474
+ this.conversationService = inject(AXConversationService);
1475
+ this.mobileView = signal('list', ...(ngDevMode ? [{ debugName: "mobileView" }] : []));
1476
+ this.hasActiveConversation = computed(() => {
1477
+ return this.conversationService.activeConversationId() !== null;
1478
+ }, ...(ngDevMode ? [{ debugName: "hasActiveConversation" }] : []));
1479
+ effect(() => {
1480
+ const activeId = this.conversationService.activeConversationId();
1481
+ if (activeId && this.isMobile()) {
1482
+ this.mobileView.set('chat');
1545
1483
  }
1546
- if (typeof avatar === 'string') {
1547
- const val = avatar.trim();
1548
- // Resolve stored file identifiers like "file:<id>" or bare file IDs
1549
- if (val.startsWith('file:')) {
1550
- try {
1551
- const fileId = val.substring(5);
1552
- const info = await this.fileStorage.getInfo(fileId);
1553
- const url = info?.url ? String(info.url) : null;
1554
- if (url) {
1555
- await this.preloadAndSetGroupAvatar(url);
1556
- }
1557
- else {
1558
- this.groupAvatarUrl.set(null);
1559
- this.isGroupAvatarLoading.set(false);
1560
- }
1561
- }
1562
- catch {
1563
- this.groupAvatarUrl.set(null);
1564
- this.isGroupAvatarLoading.set(false);
1565
- }
1566
- return;
1567
- }
1568
- // If it's a full URL or data URI, use as-is; otherwise treat as a fileId
1569
- if (/^(https?:)?\/\//i.test(val) || val.startsWith('data:')) {
1570
- await this.preloadAndSetGroupAvatar(val);
1571
- return;
1572
- }
1573
- try {
1574
- const info = await this.fileStorage.getInfo(val);
1575
- const url = info?.url ? String(info.url) : null;
1576
- if (url) {
1577
- await this.preloadAndSetGroupAvatar(url);
1578
- }
1579
- else {
1580
- this.groupAvatarUrl.set(null);
1581
- this.isGroupAvatarLoading.set(false);
1582
- }
1583
- }
1584
- catch {
1585
- this.groupAvatarUrl.set(null);
1586
- this.isGroupAvatarLoading.set(false);
1587
- }
1588
- return;
1589
- }
1590
- const source = avatar.source;
1591
- if (!source) {
1592
- this.groupAvatarUrl.set(null);
1593
- this.isGroupAvatarLoading.set(false);
1594
- return;
1595
- }
1596
- switch (source.kind) {
1597
- case 'url':
1598
- await this.preloadAndSetGroupAvatar(String(source.value));
1599
- return;
1600
- case 'blob': {
1601
- const prev = this.groupBlobUrl();
1602
- if (prev)
1603
- URL.revokeObjectURL(prev);
1604
- const blob = source.value;
1605
- const next = URL.createObjectURL(blob);
1606
- this.groupBlobUrl.set(next);
1607
- await this.preloadAndSetGroupAvatar(next);
1608
- return;
1609
- }
1610
- case 'fileId': {
1611
- try {
1612
- const info = await this.fileStorage.getInfo(String(source.value));
1613
- const url = info?.url ? String(info.url) : null;
1614
- if (url) {
1615
- await this.preloadAndSetGroupAvatar(url);
1616
- }
1617
- else {
1618
- this.groupAvatarUrl.set(null);
1619
- this.isGroupAvatarLoading.set(false);
1620
- }
1621
- }
1622
- catch {
1623
- this.groupAvatarUrl.set(null);
1624
- this.isGroupAvatarLoading.set(false);
1625
- }
1626
- return;
1627
- }
1628
- default:
1629
- this.groupAvatarUrl.set(null);
1630
- this.isGroupAvatarLoading.set(false);
1631
- return;
1632
- }
1633
- }, ...(ngDevMode ? [{ debugName: "#groupAvatarEffect" }] : []));
1634
- this.formattedLastMessageDate = computed(() => {
1635
- const lastMessage = this.data().lastMessage;
1636
- const createdAt = lastMessage?.auditInfo?.created?.at;
1637
- if (!createdAt) {
1638
- return '';
1639
- }
1640
- const messageDate = new Date(createdAt);
1641
- const today = new Date();
1642
- const isToday = messageDate.getFullYear() === today.getFullYear() &&
1643
- messageDate.getMonth() === today.getMonth() &&
1644
- messageDate.getDate() === today.getDate();
1645
- if (isToday) {
1646
- return this.datePipe.transform(messageDate, 'shortTime');
1647
- }
1648
- else {
1649
- return this.datePipe.transform(messageDate, 'mediumDate');
1650
- }
1651
- }, ...(ngDevMode ? [{ debugName: "formattedLastMessageDate" }] : []));
1652
- this.lastMessage = computed(() => {
1653
- const message = this.data().lastMessage;
1654
- if (!message)
1655
- return { content: 'No messages yet', contentType: 'text', reactions: [] };
1656
- return {
1657
- content: message.message.contentType === 'text' ? message.message.content : message.message.contentType,
1658
- contentType: message.message.contentType,
1659
- reactions: message.reactions || [],
1660
- author: message.author,
1661
- };
1662
- }, ...(ngDevMode ? [{ debugName: "lastMessage" }] : []));
1663
- this.hasUnread = computed(() => {
1664
- return this.data().unreadCount > 0;
1665
- }, ...(ngDevMode ? [{ debugName: "hasUnread" }] : []));
1666
- this.unreadCount = computed(() => {
1667
- return this.data().unreadCount.toString();
1668
- }, ...(ngDevMode ? [{ debugName: "unreadCount" }] : []));
1669
- }
1670
- #chatInfoEffect;
1671
- async preloadAndSetGroupAvatar(url) {
1672
- this.isGroupAvatarLoading.set(true);
1673
- await new Promise((resolve) => {
1674
- const img = new Image();
1675
- img.onload = () => {
1676
- this.groupAvatarUrl.set(url);
1677
- this.isGroupAvatarLoading.set(false);
1678
- resolve();
1679
- };
1680
- img.onerror = () => {
1681
- this.groupAvatarUrl.set(null);
1682
- this.isGroupAvatarLoading.set(false);
1683
- resolve();
1684
- };
1685
- img.src = url;
1686
1484
  });
1687
1485
  }
1688
- #groupAvatarEffect;
1689
- ngOnDestroy() {
1690
- const prev = this.groupBlobUrl();
1691
- if (prev)
1692
- URL.revokeObjectURL(prev);
1693
- }
1694
- onPressChatItem(id) {
1695
- this.pressChatItem.emit(id);
1696
- }
1697
- messageSeenStatus() {
1698
- const lastMessage = this.data().lastMessage;
1699
- if (!lastMessage)
1700
- return '';
1701
- // Check if current user is the author
1702
- if (this.sessionService.user?.id === lastMessage.author?.id) {
1703
- // Check if message has been seen by anyone
1704
- return lastMessage.seen.length > 0 ? 'ax-icon-dobble-check' : 'ax-icon-check';
1705
- }
1706
- return '';
1707
- }
1708
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1709
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXMChatItemComponent, isStandalone: true, selector: "axm-chat-item", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, typing: { classPropertyName: "typing", publicName: "typing", isSignal: true, isRequired: false, transformFunction: null }, lastMessageReaction: { classPropertyName: "lastMessageReaction", publicName: "lastMessageReaction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pressChatItem: "pressChatItem" }, ngImport: i0, template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n @if (chatName().isPrivate) {\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n } @else {\n @if (isGroupAvatarLoading()) {\n <div class=\"ax-w-12 ax-h-12 ax-rounded-full ax-flex ax-items-center ax-justify-center ax-bg-surface\">\n <i class=\"fa-solid fa-spinner-third fa-spin\"></i>\n </div>\n } @else if (groupAvatarUrl()) {\n <div\n class=\"ax-w-12 ax-h-12 ax-rounded-full ax-bg-center ax-bg-cover\"\n [style.backgroundImage]=\"'url(' + groupAvatarUrl() + ')'\"\n ></div>\n } @else {\n <ax-avatar [size]=\"48\">\n <ax-text class=\"ax-primary-lightest\">\n <small class=\"ax-text-xs ax-font-semibold fas fa-users\"></small>\n </ax-text>\n </ax-avatar>\n }\n }\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-1\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-py-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n @if (typing()?.isTyping) {\n <div class=\"ax-pb-1 ax-flex ax-justify-center ax-items-end ax-gap-1\">\n <span class=\"ax-text-on-surface-light ax-font-semibold ax-capitalize ax-whitespace-nowrap ax-text-sm\">\n @if (!chatName().isPrivate) {\n {{ typing()?.user }}\n }\n {{ '@conversation:is-typing' | translate | async }}\n </span>\n <div class=\"typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n </div>\n } @else {\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @if ((lastMessage().author?.fullName || lastMessage().author?.username) && !chatName().isPrivate) {\n <span class=\"ax-text-on-surface-light ax-font-semibold ax-capitalize ax-whitespace-nowrap\">\n {{ (lastMessage().author?.fullName || lastMessage().author?.username) + ': ' }}\n </span>\n }\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n }\n\n <span class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </span>\n </div>\n }\n <div class=\"ax-flex ax-items-center ax-gap-2\">\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n <!-- @if (lastMessageReaction()) {\n <div class=\"ax-flex ax-items-center ax-gap-1 ax-bg-surface ax-rounded-full ax-p-1 ax-px-2\">\n <span class=\"ax-text-sm\">{{ lastMessageReaction() }}</span>\n <span class=\"ax-text-xs\">{{ lastMessage().reactions.length }}</span>\n </div>\n } -->\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.typing-indicator{display:flex;align-items:center;padding-bottom:.4rem}.typing-indicator span{height:4px;width:4px;margin:0 1px;background-color:#9e9ea1;border-radius:50%;display:inline-block;animation:bounce 1.4s infinite ease-in-out both}.typing-indicator span:nth-child(1){animation-delay:-.32s}.typing-indicator span:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.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: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i3.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1710
- }
1711
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatItemComponent, decorators: [{
1712
- type: Component,
1713
- args: [{ selector: 'axm-chat-item', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1714
- AXDecoratorModule,
1715
- AXBadgeModule,
1716
- AXPUserAvatarComponent,
1717
- AXAvatarModule,
1718
- AXDecoratorModule,
1719
- AXTranslationModule,
1720
- AsyncPipe,
1721
- ], template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n @if (chatName().isPrivate) {\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n } @else {\n @if (isGroupAvatarLoading()) {\n <div class=\"ax-w-12 ax-h-12 ax-rounded-full ax-flex ax-items-center ax-justify-center ax-bg-surface\">\n <i class=\"fa-solid fa-spinner-third fa-spin\"></i>\n </div>\n } @else if (groupAvatarUrl()) {\n <div\n class=\"ax-w-12 ax-h-12 ax-rounded-full ax-bg-center ax-bg-cover\"\n [style.backgroundImage]=\"'url(' + groupAvatarUrl() + ')'\"\n ></div>\n } @else {\n <ax-avatar [size]=\"48\">\n <ax-text class=\"ax-primary-lightest\">\n <small class=\"ax-text-xs ax-font-semibold fas fa-users\"></small>\n </ax-text>\n </ax-avatar>\n }\n }\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-1\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-py-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n @if (typing()?.isTyping) {\n <div class=\"ax-pb-1 ax-flex ax-justify-center ax-items-end ax-gap-1\">\n <span class=\"ax-text-on-surface-light ax-font-semibold ax-capitalize ax-whitespace-nowrap ax-text-sm\">\n @if (!chatName().isPrivate) {\n {{ typing()?.user }}\n }\n {{ '@conversation:is-typing' | translate | async }}\n </span>\n <div class=\"typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n </div>\n } @else {\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @if ((lastMessage().author?.fullName || lastMessage().author?.username) && !chatName().isPrivate) {\n <span class=\"ax-text-on-surface-light ax-font-semibold ax-capitalize ax-whitespace-nowrap\">\n {{ (lastMessage().author?.fullName || lastMessage().author?.username) + ': ' }}\n </span>\n }\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n }\n\n <span class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </span>\n </div>\n }\n <div class=\"ax-flex ax-items-center ax-gap-2\">\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n <!-- @if (lastMessageReaction()) {\n <div class=\"ax-flex ax-items-center ax-gap-1 ax-bg-surface ax-rounded-full ax-p-1 ax-px-2\">\n <span class=\"ax-text-sm\">{{ lastMessageReaction() }}</span>\n <span class=\"ax-text-xs\">{{ lastMessage().reactions.length }}</span>\n </div>\n } -->\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.typing-indicator{display:flex;align-items:center;padding-bottom:.4rem}.typing-indicator span{height:4px;width:4px;margin:0 1px;background-color:#9e9ea1;border-radius:50%;display:inline-block;animation:bounce 1.4s infinite ease-in-out both}.typing-indicator span:nth-child(1){animation-delay:-.32s}.typing-indicator span:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}\n"] }]
1722
- }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], typing: [{ type: i0.Input, args: [{ isSignal: true, alias: "typing", required: false }] }], lastMessageReaction: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastMessageReaction", required: false }] }], pressChatItem: [{ type: i0.Output, args: ["pressChatItem"] }] } });
1723
-
1724
- class AXMChatComponent extends AXPPageLayoutBaseComponent {
1725
- constructor() {
1726
- super(...arguments);
1727
- this.activatedRoute = inject(ActivatedRoute);
1728
- this.chatService = inject(AXMChatManagementService);
1729
- this.realtimeService = inject(AXMChatRealtimeService);
1730
- this.dialogService = inject(AXDialogService);
1731
- this.layoutBuilder = inject(AXPLayoutBuilderService);
1732
- this.router = inject(Router);
1733
- this.unsubscribe = inject(AXUnsubscriber);
1734
- this.notificationConnectorService = inject(AXMNotificationConnectorService);
1735
- this.dataSelectorService = inject(AXPEntityDataSelectorService);
1736
- this.entityRegistry = inject(AXPEntityDefinitionRegistryService);
1737
- // View Children
1738
- this.tab = viewChild('tab', ...(ngDevMode ? [{ debugName: "tab" }] : []));
1739
- this.container = viewChild('container', ...(ngDevMode ? [{ debugName: "container" }] : []));
1740
- this.chatContext = viewChild('chatContext', ...(ngDevMode ? [{ debugName: "chatContext" }] : []));
1741
- this.tabContext = viewChild('tabContext', ...(ngDevMode ? [{ debugName: "tabContext" }] : []));
1742
- // State signals
1743
- this.rooms = signal([], ...(ngDevMode ? [{ debugName: "rooms" }] : []));
1744
- this.allRooms = signal([], ...(ngDevMode ? [{ debugName: "allRooms" }] : []));
1745
- this.tabs = signal([], ...(ngDevMode ? [{ debugName: "tabs" }] : []));
1746
- this.selectedRoom = signal(null, ...(ngDevMode ? [{ debugName: "selectedRoom" }] : []));
1747
- this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1748
- this.typingStatus = signal({}, ...(ngDevMode ? [{ debugName: "typingStatus" }] : []));
1749
- this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
1750
- this.activeTabIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeTabIndex" }] : []));
1751
- this.isSearching = signal(false, ...(ngDevMode ? [{ debugName: "isSearching" }] : []));
1752
- this.searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
1753
- this.showSearch = signal(true, ...(ngDevMode ? [{ debugName: "showSearch" }] : []));
1754
- this.placeholder = signal('@conversation:search.placeholder', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1755
- // Localized labels (preloaded for sync menu building)
1756
- this.tOpen = signal('Open', ...(ngDevMode ? [{ debugName: "tOpen" }] : []));
1757
- this.tMarkSeen = signal('Mark as seen', ...(ngDevMode ? [{ debugName: "tMarkSeen" }] : []));
1758
- this.tTab = signal('Tab', ...(ngDevMode ? [{ debugName: "tTab" }] : []));
1759
- this.tRemoveFromTab = signal('Remove from tab', ...(ngDevMode ? [{ debugName: "tRemoveFromTab" }] : []));
1760
- this.tDelete = signal('Delete', ...(ngDevMode ? [{ debugName: "tDelete" }] : []));
1761
- this.tRemoveChat = signal('Remove Chat', ...(ngDevMode ? [{ debugName: "tRemoveChat" }] : []));
1762
- // Computed signals
1763
- this.activeTab = computed(() => {
1764
- const tabs = this.tabs();
1765
- const index = this.activeTabIndex();
1766
- if (tabs.length > 0 && index >= 0 && index < tabs.length) {
1767
- return tabs[index];
1768
- }
1769
- return undefined;
1770
- }, ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
1771
- this.filteredRooms = signal([], ...(ngDevMode ? [{ debugName: "filteredRooms" }] : []));
1772
- this.searchEffect = effect(() => {
1773
- const allRooms = this.allRooms();
1774
- let chatRooms = [...allRooms]; // Start with all rooms
1775
- // Tab filter
1776
- const activeTab = this.activeTab();
1777
- if (activeTab) {
1778
- if (activeTab.name === 'unread') {
1779
- // Special local state for unread
1780
- chatRooms = allRooms.filter((room) => room.unreadCount > 0);
1781
- }
1782
- else if (activeTab.name === 'all') {
1783
- // Local state for all
1784
- chatRooms = allRooms;
1785
- }
1786
- else {
1787
- // For other tabs defined by the service
1788
- const includedIds = new Set(activeTab.includeRooms.map((r) => r.id));
1789
- chatRooms = allRooms.filter((room) => includedIds.has(room.id));
1790
- }
1791
- }
1792
- // Search filter (applies on top of tab filter)
1793
- const query = (this.searchQuery() || '').toLowerCase();
1794
- if (query) {
1795
- const filtered = [];
1796
- for (const room of chatRooms) {
1797
- try {
1798
- let roomName = '';
1799
- // Handle group chats vs. single-user chats
1800
- if (room.members && room.members.length > 1) {
1801
- roomName = room.title || '';
1802
- }
1803
- else if (room.members && room.members.length === 1) {
1804
- const member = room.members[0];
1805
- if (member) {
1806
- roomName = member.fullName || '';
1807
- }
1808
- }
1809
- const lastMessage = room.lastMessage?.message?.content || '';
1810
- if (roomName.toLowerCase().includes(query) || lastMessage.toLowerCase().includes(query)) {
1811
- filtered.push(room);
1812
- }
1813
- }
1814
- catch (error) {
1815
- console.error(`Failed to process search for room ${room.id}`, error);
1816
- }
1817
- }
1818
- chatRooms = filtered;
1819
- }
1820
- this.filteredRooms.set(chatRooms);
1821
- }, ...(ngDevMode ? [{ debugName: "searchEffect" }] : []));
1822
- this.unreadCount = computed(() => this.allRooms().filter((i) => i.unreadCount > 0).length, ...(ngDevMode ? [{ debugName: "unreadCount" }] : []));
1823
- this.allCount = computed(() => this.allRooms().length, ...(ngDevMode ? [{ debugName: "allCount" }] : []));
1824
- this.hasUnread = computed(() => this.allRooms().some((i) => i.unreadCount > 0), ...(ngDevMode ? [{ debugName: "hasUnread" }] : []));
1825
- this.totalCount = computed(() => this.allRooms().length || 0, ...(ngDevMode ? [{ debugName: "totalCount" }] : []));
1826
- // Context menus (desktop)
1827
- this.contextRoom = null;
1828
- this.contextTab = null;
1829
- this.contextTabIndex = -1;
1830
- }
1831
- async deleteTab(tab) {
1832
- const result = await this.dialogService.confirm(await this.translateService.translateAsync('@conversation:confirmations.delete-tab.title'), await this.translateService.translateAsync('@conversation:confirmations.delete-tab.message', {
1833
- params: { title: tab.title },
1834
- }));
1835
- if (result.result) {
1836
- await this.chatService.deleteTab(tab.id);
1837
- await this.loadTabs();
1838
- }
1839
- }
1840
- async ngOnInit() {
1841
- super.ngOnInit();
1842
- this.isLoading.set(true);
1843
- this.error.set(null);
1844
- const [openText, markSeenText, tabText, removeFromTabText, deleteText, removeChatText] = await Promise.all([
1845
- this.translateService.translateAsync('@conversation:actions.open'),
1846
- this.translateService.translateAsync('@conversation:actions.mark-seen'),
1847
- this.translateService.translateAsync('@conversation:actions.tab'),
1848
- this.translateService.translateAsync('@conversation:actions.remove-from-tab'),
1849
- this.translateService.translateAsync('@conversation:actions.delete'),
1850
- this.translateService.translateAsync('@conversation:remove-chat'),
1851
- ]);
1852
- this.tOpen.set(openText);
1853
- this.tMarkSeen.set(markSeenText);
1854
- this.tTab.set(tabText);
1855
- this.tRemoveFromTab.set(removeFromTabText);
1856
- this.tDelete.set(deleteText);
1857
- this.tRemoveChat.set(removeChatText);
1858
- await Promise.all([this.loadChats(), this.loadTabs()]);
1859
- this.isLoading.set(false);
1860
- //
1861
- this.router.events
1862
- .pipe(this.unsubscribe.takeUntilDestroy, filter((event) => event instanceof NavigationEnd), startWith(null))
1863
- .subscribe(async () => {
1864
- await this.loadFromRoute();
1865
- });
1866
- //
1867
- this.realtimeService.room$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((event) => {
1868
- switch (event.type) {
1869
- case 'add':
1870
- this.allRooms.update((rooms) => [event.payload, ...rooms.filter((r) => r.id !== event.payload.id)]);
1871
- break;
1872
- case 'remove':
1873
- this.allRooms.update((rooms) => rooms.filter((r) => r.id !== event.payload));
1874
- if (event.payload === this.selectedRoom()?.id) {
1875
- this.selectedRoom.set(null);
1876
- this.router.navigate(['./'], { relativeTo: this.activatedRoute });
1877
- }
1878
- break;
1879
- case 'update':
1880
- this.allRooms.update((rooms) => rooms.map((r) => (r.id === event.payload.id ? event.payload : r)));
1881
- break;
1882
- case 'seen':
1883
- this.allRooms.update((rooms) => rooms.map((room) => (room.id === event.payload ? { ...room, unreadCount: 0 } : room)));
1884
- break;
1885
- }
1886
- });
1887
- this.realtimeService.message$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((event) => {
1888
- switch (event.type) {
1889
- case 'add': {
1890
- const message = event.payload;
1891
- const rooms = this.allRooms();
1892
- const roomIndex = rooms.findIndex((r) => r.id === message.roomId);
1893
- if (roomIndex > -1) {
1894
- const roomToUpdate = rooms[roomIndex];
1895
- const isCurrentUser = message.author.id === this.sessionService.user?.id;
1896
- const updatedRoom = {
1897
- ...roomToUpdate,
1898
- lastMessage: message,
1899
- unreadCount: !isCurrentUser ? (roomToUpdate.unreadCount || 0) + 1 : roomToUpdate.unreadCount,
1900
- };
1901
- const newRooms = [updatedRoom, ...rooms.filter((r) => r.id !== message.roomId)];
1902
- this.allRooms.set(newRooms);
1903
- if (!isCurrentUser && (this.selectedRoom()?.id !== message.roomId || document.hidden)) {
1904
- const notification = convertChatMessageToNotification(roomToUpdate, message, this.sessionService.user);
1905
- this.notificationConnectorService.create({
1906
- ...notification,
1907
- id: message.id,
1908
- entityName: 'notifications',
1909
- createAt: new Date(),
1910
- }, {
1911
- onClick: () => {
1912
- this.router.navigate([roomToUpdate.id], { relativeTo: this.activatedRoute });
1913
- },
1914
- activeWindow: true,
1915
- });
1916
- }
1917
- }
1918
- else {
1919
- this.chatService.getRoom(message.roomId).then((newRoom) => {
1920
- this.allRooms.update((currentRooms) => [newRoom, ...currentRooms]);
1921
- });
1922
- }
1923
- break;
1924
- }
1925
- case 'update': {
1926
- const message = event.payload;
1927
- this.allRooms.update((rooms) => {
1928
- return rooms.map((room) => {
1929
- if (room.lastMessage?.id === message.id) {
1930
- return {
1931
- ...room,
1932
- lastMessage: {
1933
- ...room.lastMessage,
1934
- ...message,
1935
- },
1936
- };
1937
- }
1938
- return room;
1939
- });
1940
- });
1941
- break;
1942
- }
1943
- }
1944
- });
1945
- this.realtimeService.typingStatus$.pipe(this.unsubscribe.takeUntilDestroy).subscribe(async (status) => {
1946
- const currentUserId = this.sessionService.user?.id;
1947
- if (status.user.id !== currentUserId) {
1948
- if (status.isTyping) {
1949
- this.typingStatus.update((value) => ({
1950
- ...value,
1951
- [status.roomId]: { user: status.user.fullName || '', isTyping: true },
1952
- }));
1953
- }
1954
- else {
1955
- this.typingStatus.update((value) => {
1956
- const { [status.roomId]: _, ...rest } = value;
1957
- return rest;
1958
- });
1959
- }
1960
- }
1961
- });
1962
- }
1963
- async loadFromRoute() {
1964
- const chatId = this.activatedRoute.snapshot.firstChild?.paramMap.get('id');
1965
- if (chatId) {
1966
- this.openChat(chatId);
1967
- }
1968
- else {
1969
- this.selectedRoom.set(null);
1970
- }
1971
- }
1972
- // Page Interface Implementation
1973
- async getPageTitle() {
1974
- const room = this.selectedRoom();
1975
- if (!room && this.layoutService.isMobileDevice()) {
1976
- return await this.translateService.translateAsync('@conversation:plural-title');
1977
- }
1978
- if (!room) {
1979
- return '';
1980
- }
1981
- const { members, title, id } = room;
1982
- if (members.length > 1) {
1983
- return title || (await this.translateService.translateAsync('@conversation:labels.group-chat'));
1984
- }
1985
- const member = members[0];
1986
- if (!member) {
1987
- return await this.translateService.translateAsync('@conversation:labels.unknown-user');
1988
- }
1989
- return member.fullName || (await this.translateService.translateAsync('@conversation:labels.unknown-user'));
1990
- }
1991
- getPageDescription() {
1992
- const typing = this.typingStatus();
1993
- const selectedRoomId = this.selectedRoom()?.id;
1994
- if (selectedRoomId && typing[selectedRoomId]?.isTyping) {
1995
- return `${typing[selectedRoomId].user} is typing...`;
1996
- }
1997
- const room = this.selectedRoom();
1998
- return room?.topic || '';
1999
- }
2000
- async getBackButton() {
2001
- if (this.selectedRoom()) {
2002
- return {
2003
- title: await this.translateService.translateAsync('@conversation:back-to-chat-list'),
2004
- };
2005
- }
2006
- else
2007
- return null;
2008
- }
2009
- async getSecondaryMenuItems() {
2010
- const items = [];
2011
- const hasRoom = !!this.selectedRoom();
2012
- if (hasRoom) {
2013
- const room = this.selectedRoom();
2014
- const currentUserId = this.sessionService.user?.id;
2015
- const creatorId = room.auditInfo?.created?.by?.id;
2016
- const isCreator = !!currentUserId && !!creatorId && currentUserId === creatorId;
2017
- const isGroup = (room.members?.length ?? 0) > 1;
2018
- const isChannel = room.topic === 'channel';
2019
- const kindKey = isGroup ? '@conversation:labels.group-chat' : '@conversation:room.individual-title';
2020
- const kindText = await this.translateService.translateAsync(kindKey);
2021
- const deleteText = await this.translateService.translateAsync('@conversation:actions.delete');
2022
- const removeChatText = await this.translateService.translateAsync('@conversation:remove-chat');
2023
- items.push({
2024
- title: 'Edit Room',
2025
- icon: 'fa-light fa-pen-to-square',
2026
- command: { name: 'edit-room' },
2027
- }, {
2028
- title: isCreator ? `${deleteText} ${kindText}` : removeChatText,
2029
- icon: 'fa-light fa-trash-can',
2030
- color: 'danger',
2031
- command: { name: isCreator ? 'delete-room' : 'leave-room' },
2032
- });
2033
- }
2034
- return items;
2035
- }
2036
- async execute(command) {
2037
- switch (command.name) {
2038
- case 'remove-chat':
2039
- const response = await this.dialogService.confirm(await this.translateService.translateAsync('@conversation:confirmations.remove-chat.title'), await this.translateService.translateAsync('@conversation:confirmations.remove-chat.message'));
2040
- if (response.result) {
2041
- await this.chatService.deleteRoom(this.selectedRoom()?.id);
2042
- this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2043
- }
2044
- break;
2045
- case 'edit-room': {
2046
- const room = await this.chatService.getRoom(this.selectedRoom()?.id);
2047
- if (!room)
2048
- return;
2049
- const currentMemberIds = (room.members || []).map((m) => m.id);
2050
- const dialogRef = await this.layoutBuilder
2051
- .create()
2052
- .dialog(dialog => {
2053
- dialog
2054
- .setTitle('Edit Room')
2055
- .setSize('sm')
2056
- .setContext({ title: room.title || '', members: currentMemberIds, avatar: room.avatar })
2057
- .content(layoutBuilder => {
2058
- layoutBuilder.flex(flex => {
2059
- flex
2060
- .setDirection('column')
2061
- .formField('@conversation:new-conversation.title.label', field => {
2062
- field.path('title');
2063
- field.textBox({ placeholder: '@conversation:new-conversation.title.placeholder' });
2064
- })
2065
- .formField('@conversation:new-conversation.lookup.label', field => {
2066
- field.path('members');
2067
- field.lookupBox({
2068
- entity: 'SecurityManagement.User',
2069
- multiple: true,
2070
- customFilter: {
2071
- field: 'id',
2072
- operator: { type: 'notEqual' },
2073
- value: this.sessionService.user?.id,
2074
- },
2075
- });
2076
- })
2077
- .formField('Avatar', field => {
2078
- field.path('avatar');
2079
- field.customWidget('avatar', {});
2080
- });
2081
- });
2082
- })
2083
- .setActions(actions => {
2084
- actions.cancel('@general:actions.cancel.title').submit('Save');
2085
- });
2086
- })
2087
- .show();
2088
- const ctx = dialogRef.context();
2089
- const action = dialogRef.action();
2090
- dialogRef.close();
2091
- if (action === 'cancel')
2092
- return;
2093
- const selectedIds = Array.isArray(ctx.members) ? ctx.members : [];
2094
- const toAdd = selectedIds.filter((id) => !currentMemberIds.includes(id));
2095
- const toRemove = currentMemberIds.filter((id) => !selectedIds.includes(id));
2096
- const trimmedTitle = typeof ctx.title === 'string' ? ctx.title.trim() : undefined;
2097
- const titleChanged = typeof trimmedTitle === 'string' && trimmedTitle !== (room.title || '').trim();
2098
- const avatarChanged = (ctx.avatar ?? null) !== (room.avatar ?? null);
2099
- const updatePayload = {};
2100
- if (titleChanged) {
2101
- updatePayload.title = trimmedTitle;
2102
- }
2103
- if (avatarChanged) {
2104
- updatePayload.avatar = ctx.avatar ?? null;
2105
- }
2106
- const ops = [];
2107
- if (Object.keys(updatePayload).length > 0) {
2108
- ops.push(this.chatService.updateRoom(room.id, updatePayload));
2109
- }
2110
- toAdd.forEach((id) => ops.push(this.chatService.addParticipant(room.id, id)));
2111
- toRemove.forEach((id) => ops.push(this.chatService.removeParticipant(room.id, id)));
2112
- try {
2113
- if (ops.length) {
2114
- await Promise.all(ops);
2115
- await this.loadChats();
2116
- }
2117
- }
2118
- catch (e) {
2119
- this.toastService.danger('Failed to update room');
2120
- }
2121
- break;
2122
- }
2123
- case 'delete-room': {
2124
- const room = this.selectedRoom();
2125
- if (!room)
2126
- return;
2127
- const response = await this.dialogService.confirm(await this.translateService.translateAsync('@conversation:confirmations.delete-room.title'), await this.translateService.translateAsync('@conversation:confirmations.delete-room.message'));
2128
- if (response.result) {
2129
- await this.chatService.deleteRoom(room.id);
2130
- this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2131
- }
2132
- break;
2133
- }
2134
- case 'leave-room': {
2135
- const room = this.selectedRoom();
2136
- if (!room)
2137
- return;
2138
- const currentUserId = this.sessionService.user?.id;
2139
- const response = await this.dialogService.confirm(await this.translateService.translateAsync('@conversation:confirmations.remove-chat.title'), await this.translateService.translateAsync('@conversation:confirmations.remove-chat.message'));
2140
- if (response.result) {
2141
- await this.chatService.removeParticipant(room.id, currentUserId);
2142
- this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2143
- }
2144
- break;
2145
- }
2146
- }
2147
- }
2148
- onBackButtonClick() {
2149
- this.selectedRoom.set(null);
2150
- this.typingStatus.update((value) => {
2151
- const { [this.selectedRoom()?.id]: _, ...rest } = value;
2152
- return rest;
2153
- });
2154
- this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2155
- }
2156
- // Methods
2157
- getRoomCountForTab(tab) {
2158
- const allRooms = this.allRooms();
2159
- // For all tab types, only count rooms that have unread messages
2160
- if (tab.name === 'all' || tab.name === 'unread') {
2161
- return allRooms.filter((room) => room.unreadCount > 0).length;
2162
- }
2163
- const includedIds = new Set(tab.includeRooms.map((r) => r.id));
2164
- return allRooms.filter((room) => includedIds.has(room.id) && room.unreadCount > 0).length;
2165
- }
2166
- async loadTabs() {
2167
- const localTabs = [
2168
- {
2169
- id: 'all-local',
2170
- name: 'all',
2171
- title: 'All',
2172
- excludeRooms: [],
2173
- includeRooms: [],
2174
- },
2175
- {
2176
- id: 'unread-local',
2177
- name: 'unread',
2178
- title: 'Unread',
2179
- excludeRooms: [],
2180
- includeRooms: [],
2181
- },
2182
- ];
2183
- try {
2184
- const result = await this.chatService.listTabs();
2185
- this.tabs.set([...localTabs, ...result.items]);
2186
- }
2187
- catch (error) {
2188
- console.error('Failed to load tabs:', error);
2189
- this.tabs.set(localTabs);
2190
- }
2191
- }
2192
- async loadChats() {
2193
- try {
2194
- // Use the updated service method to get rooms with last messages
2195
- const response = await this.chatService.listRooms();
2196
- // Sort rooms by last message created.at timestamp in descending order
2197
- const sortedRooms = response.items.sort((a, b) => {
2198
- const dateA = a.lastMessage?.auditInfo?.created?.at || a.auditInfo?.created?.at || 0;
2199
- const dateB = b.lastMessage?.auditInfo?.created?.at || b.auditInfo?.created?.at || 0;
2200
- return new Date(dateB).getTime() - new Date(dateA).getTime();
2201
- });
2202
- this.allRooms.set(sortedRooms);
2203
- this.rooms.set(sortedRooms);
2204
- }
2205
- catch (error) {
2206
- this.error.set('Failed to load chats. Please try again.');
2207
- console.error('Error loading chats:', error);
2208
- }
2209
- }
2210
- async refreshChat() {
2211
- await this.loadChats();
2212
- }
2213
- async openChat(chatId) {
2214
- try {
2215
- this.typingStatus.update((value) => {
2216
- const { [chatId]: _, ...rest } = value;
2217
- return rest;
2218
- });
2219
- // Use the service method to mark chat as read
2220
- await this.chatService.markRoomSeen(chatId);
2221
- // Update local state without making another network request
2222
- const currentRooms = this.allRooms();
2223
- const updatedRooms = currentRooms.map((room) => {
2224
- if (room.id === chatId) {
2225
- // Reset unread count for the selected room
2226
- return { ...room, unreadCount: 0 };
2227
- }
2228
- return room;
2229
- });
2230
- // Update the rooms signals
2231
- this.allRooms.set(updatedRooms);
2232
- this.rooms.set(updatedRooms);
2233
- // Find the selected room
2234
- const selectedRoom = updatedRooms.find((room) => room.id === chatId) || null;
2235
- this.selectedRoom.set(selectedRoom);
2236
- // Navigate to the chat details
2237
- this.router.navigate([chatId], { relativeTo: this.activatedRoute });
2238
- }
2239
- catch (error) {
2240
- console.error('Failed to mark chat as read:', error);
2241
- }
2242
- }
2243
- searchChat(query) {
2244
- this.searchQuery.set(query);
2245
- this.isSearching.set(!!query);
2246
- }
2247
- // Header methods
2248
- onSearch(query) {
2249
- this.searchChat(query);
2250
- }
2251
- clearSearch() {
2252
- this.searchQuery.set('');
2253
- this.isSearching.set(false);
2254
- }
2255
- async openSelector() {
2256
- try {
2257
- const entity = await this.entityRegistry.resolve('SecurityManagement', 'User');
2258
- const result = await this.dataSelectorService.open({
2259
- entity,
2260
- title: 'Select Users',
2261
- allowMultiple: true,
2262
- columns: ['username', 'displayName', 'roleIds'],
2263
- filters: {
2264
- field: 'id',
2265
- operator: { type: 'equal' },
2266
- // value: this.sessionService.user?.id,
2267
- value: '{{ session.currentUser().id }}',
1486
+ goBackToList() {
1487
+ this.mobileView.set('list');
1488
+ }
1489
+ isMobile() {
1490
+ return window.innerWidth < 768;
1491
+ }
1492
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1493
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", providers: [
1494
+ ...provideConversation({
1495
+ userApi: AXIndexedDBUserApi,
1496
+ conversationApi: AXIndexedDBConversationApi,
1497
+ messageApi: AXIndexedDBMessageAIApi,
1498
+ realtimeApi: AXIndexedDBRealtimeApi,
1499
+ config: {},
1500
+ registry: {
1501
+ messageRenderers: [
1502
+ AX_CONVERSATION_TEXT_RENDERER,
1503
+ AX_CONVERSATION_IMAGE_RENDERER,
1504
+ AX_CONVERSATION_VIDEO_RENDERER,
1505
+ AX_CONVERSATION_AUDIO_RENDERER,
1506
+ AX_CONVERSATION_VOICE_RENDERER,
1507
+ AX_CONVERSATION_FILE_RENDERER,
1508
+ AX_CONVERSATION_LOCATION_RENDERER,
1509
+ AX_CONVERSATION_STICKER_RENDERER,
1510
+ AX_CONVERSATION_CONTACT_RENDERER,
1511
+ ],
1512
+ messageActions: [
1513
+ AX_CONVERSATION_MESSAGE_REPLY_ACTION,
1514
+ AX_CONVERSATION_MESSAGE_FORWARD_ACTION,
1515
+ AX_CONVERSATION_MESSAGE_EDIT_ACTION,
1516
+ AX_CONVERSATION_MESSAGE_DELETE_ACTION,
1517
+ ],
1518
+ composerActions: [
1519
+ AX_CONVERSATION_COMPOSER_EMOJI_ACTION,
1520
+ AX_CONVERSATION_COMPOSER_IMAGE_ACTION,
1521
+ AX_CONVERSATION_COMPOSER_VIDEO_ACTION,
1522
+ AX_CONVERSATION_COMPOSER_FILE_ACTION,
1523
+ AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION,
1524
+ AX_CONVERSATION_COMPOSER_AUDIO_ACTION,
1525
+ AX_CONVERSATION_COMPOSER_CONTACT_ACTION,
1526
+ AX_CONVERSATION_COMPOSER_LOCATION_ACTION,
1527
+ ],
1528
+ composerTabs: [AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_STICKER_TAB],
1529
+ conversationTabs: [
1530
+ AX_CONVERSATION_TAB_ALL,
1531
+ AX_CONVERSATION_TAB_PRIVATE,
1532
+ AX_CONVERSATION_TAB_GROUPS,
1533
+ AX_CONVERSATION_TAB_CHANNELS,
1534
+ AX_CONVERSATION_TAB_UNREAD,
1535
+ AX_CONVERSATION_TAB_ARCHIVED,
1536
+ ],
1537
+ infoBarActions: [
1538
+ AX_CONVERSATION_INFO_BAR_SEARCH_ACTION,
1539
+ AX_CONVERSATION_INFO_BAR_INFO_ACTION,
1540
+ AX_CONVERSATION_INFO_BAR_MUTE_ACTION,
1541
+ AX_CONVERSATION_INFO_BAR_DIVIDER,
1542
+ AX_CONVERSATION_INFO_BAR_DELETE_ACTION,
1543
+ AX_CONVERSATION_INFO_BAR_BLOCK_ACTION,
1544
+ ],
1545
+ conversationItemActions: [
1546
+ AX_CONVERSATION_ITEM_MUTE_ACTION,
1547
+ AX_CONVERSATION_ITEM_PIN_ACTION,
1548
+ AX_CONVERSATION_ITEM_MARK_READ_ACTION,
1549
+ AX_CONVERSATION_ITEM_DIVIDER,
1550
+ AX_CONVERSATION_ITEM_DELETE_ACTION,
1551
+ AX_CONVERSATION_ITEM_BLOCK_ACTION,
1552
+ ],
2268
1553
  },
2269
- });
2270
- return Array.isArray(result?.items)
2271
- ? result.items
2272
- .map((u) => u?.id)
2273
- .filter((id) => typeof id === 'string' && id.length > 0)
2274
- : [];
2275
- }
2276
- catch {
2277
- return [];
2278
- }
2279
- }
2280
- // Footer methods
2281
- async onNewConversation() {
2282
- const preselectedUserIds = await this.openSelector();
2283
- if (!preselectedUserIds.length) {
2284
- return;
2285
- }
2286
- let members = preselectedUserIds;
2287
- let title;
2288
- let avatar;
2289
- if (preselectedUserIds.length > 1) {
2290
- const dialogRef = await this.layoutBuilder
2291
- .create()
2292
- .dialog(dialog => {
2293
- dialog
2294
- .setTitle('@conversation:new-conversation.header')
2295
- .setSize('sm')
2296
- .setContext({ lookup: preselectedUserIds, title: '', avatar: null })
2297
- .content(layoutBuilder => {
2298
- layoutBuilder.flex(flex => {
2299
- flex
2300
- .setDirection('column')
2301
- .formField('@conversation:new-conversation.lookup.label', field => {
2302
- field.path('lookup');
2303
- field.lookupBox({ entity: 'SecurityManagement.User', multiple: true });
2304
- })
2305
- .formField('@conversation:new-conversation.title.label', field => {
2306
- field.path('title');
2307
- field.visible(`{{!(context.eval('lookup')?.length > 1) }}`);
2308
- field.textBox({
2309
- placeholder: '@conversation:new-conversation.title.placeholder',
2310
- });
2311
- })
2312
- .formField('Avatar', field => {
2313
- field.path('avatar');
2314
- field.customWidget('avatar', { refId: 'group', refType: 'group' });
2315
- });
2316
- });
2317
- })
2318
- .setActions(actions => {
2319
- actions.cancel('@general:actions.cancel.title').submit('@conversation:new-conversation.actions.accept-send');
2320
- });
2321
- })
2322
- .show();
2323
- const ctx = dialogRef.context();
2324
- const action = dialogRef.action();
2325
- dialogRef.close();
2326
- const selected = ctx?.lookup ?? [];
2327
- if (action === 'cancel' || !selected.length) {
2328
- return;
2329
- }
2330
- members = selected;
2331
- title = ctx?.title;
2332
- avatar = ctx?.avatar;
2333
- }
2334
- try {
2335
- const res = await this.chatService.createRoom(members, title, avatar || undefined);
2336
- await this.refreshChat();
2337
- this.router.navigate([res.id], { relativeTo: this.activatedRoute });
2338
- }
2339
- catch (error) {
2340
- this.toastService.danger('Failed to create room');
2341
- }
2342
- }
2343
- // Sliding item actions (mobile)
2344
- onSlideAction(room, action) {
2345
- switch (action) {
2346
- // case 'mute':
2347
- // this.toastService.warning('Mute not implemented yet');
2348
- // break;
2349
- case 'delete':
2350
- this.deleteRoom(room.id);
2351
- break;
2352
- }
2353
- }
2354
- async deleteRoom(roomId) {
2355
- try {
2356
- const dialogResult = await this.dialogService.confirm('Delete Room', 'Are you sure you want to delete Room?', 'danger', 'horizontal');
2357
- if (dialogResult.result) {
2358
- this.chatService.deleteRoom(roomId);
2359
- }
2360
- }
2361
- catch (error) {
2362
- console.error(`Error Deleting Room with Room id: ${roomId}`, error);
2363
- }
2364
- }
2365
- openContextMenu(ev, room) {
2366
- ev.preventDefault();
2367
- this.contextRoom = room;
2368
- this.chatContext()?.showAt({ x: ev.clientX, y: ev.clientY });
2369
- }
2370
- openTabContextMenu(ev, tab, index) {
2371
- ev.preventDefault();
2372
- this.contextTab = tab;
2373
- this.contextTabIndex = index;
2374
- this.tabContext()?.showAt({ x: ev.clientX, y: ev.clientY });
2375
- }
2376
- onContextOpening(e) {
2377
- const items = [];
2378
- // General Items
2379
- items.push({ name: 'open', text: this.tOpen(), icon: 'fa-light fa-message-lines' });
2380
- // Resolve context room once
2381
- const contextRoom = this.contextRoom;
2382
- // Mark as seen (only if the room has unread messages)
2383
- if (contextRoom && (contextRoom.unreadCount || 0) > 0) {
2384
- items.push({ name: 'mark-seen', text: this.tMarkSeen(), icon: 'fa-light fa-envelope-open' });
2385
- }
2386
- // Tab submenu behavior (Telegram-like)
2387
- const allTabs = this.tabs();
2388
- const activeTab = this.activeTab();
2389
- const customTabs = allTabs.filter((t) => t.name !== 'all' && t.name !== 'unread');
2390
- if (contextRoom && customTabs.length > 0) {
2391
- if (activeTab?.name === 'all') {
2392
- const tabMenuItems = customTabs.map((t) => {
2393
- const isIncluded = (t.includeRooms || []).some((r) => r.id === contextRoom.id);
2394
- return {
2395
- name: `tab.toggle:${t.id}`,
2396
- text: t.title,
2397
- icon: isIncluded ? 'fa-light fa-check' : undefined,
2398
- };
2399
- });
2400
- items.push({ name: 'tab', text: this.tTab(), icon: 'fa-light fa-folder-tree', items: tabMenuItems });
2401
- }
2402
- else if (activeTab?.name === 'unread') {
2403
- // No tab submenu in unread
2404
- }
2405
- else {
2406
- // Only remove from current custom tab
2407
- items.push({
2408
- name: 'tab.remove-current',
2409
- text: this.tRemoveFromTab(),
2410
- icon: 'fa-light fa-square-minus',
2411
- color: 'warning',
2412
- });
2413
- }
2414
- }
2415
- // Dangerous action at the end
2416
- if (contextRoom) {
2417
- const currentUserId = this.sessionService.user?.id;
2418
- const creatorId = contextRoom.auditInfo?.created?.by?.id;
2419
- const isCreator = !!currentUserId && !!creatorId && currentUserId === creatorId;
2420
- const text = isCreator ? this.tDelete() : this.tRemoveChat();
2421
- const name = isCreator ? 'delete-room' : 'leave-room';
2422
- items.push({ name, text, icon: 'fa-light fa-trash-can', color: 'danger' });
2423
- }
2424
- e.items = items;
2425
- }
2426
- // Tab header context menu (synchronous build)
2427
- onTabContextOpening(e) {
2428
- const items = [];
2429
- const tab = this.contextTab;
2430
- if (!tab) {
2431
- e.items = items;
2432
- return;
2433
- }
2434
- if (tab.name === 'all' || tab.name === 'unread') {
2435
- items.push({ name: 'tab.add', text: 'Add tab', icon: 'fa-light fa-square-plus' });
2436
- }
2437
- else {
2438
- items.push({ name: 'tab.remove', text: 'Remove tab', icon: 'fa-light fa-trash-can', color: 'danger' });
2439
- }
2440
- e.items = items;
2441
- }
2442
- async onTabContextItemClick(e) {
2443
- if (!this.contextTab)
2444
- return;
2445
- switch (e.item.name) {
2446
- case 'tab.add': {
2447
- // Build dialog for creating a new custom tab
2448
- const rooms = this.allRooms();
2449
- const roomOptions = rooms.map((r) => ({
2450
- id: r.id,
2451
- title: (r.members?.length ?? 0) > 1 ? r.title || '' : r.members?.[0]?.fullName || '',
2452
- }));
2453
- const dialogRef = await this.layoutBuilder
2454
- .create()
2455
- .dialog(dialog => {
2456
- dialog
2457
- .setTitle('New Tab')
2458
- .setSize('sm')
2459
- .setContext({ title: '', rooms: [] })
2460
- .content(layoutBuilder => {
2461
- layoutBuilder.flex(flex => {
2462
- flex
2463
- .setDirection('column')
2464
- .formField('Title', field => {
2465
- field.path('title');
2466
- field.textBox({ placeholder: 'Enter tab title' });
2467
- })
2468
- .formField('Chats', field => {
2469
- field.path('rooms');
2470
- field.selectBox({ valueField: 'id', textField: 'title', dataSource: roomOptions, multiple: true });
2471
- });
2472
- });
2473
- })
2474
- .setActions(actions => {
2475
- actions.cancel('Cancel').submit('Create');
2476
- });
2477
- })
2478
- .show();
2479
- const action = dialogRef.action();
2480
- const ctx = dialogRef.context();
2481
- dialogRef.close();
2482
- if (action === 'cancel' || !ctx?.title)
2483
- return;
2484
- try {
2485
- const name = ctx.title.trim().toLowerCase().replace(/\s+/g, '-').slice(0, 64);
2486
- const selected = Array.isArray(ctx.rooms) ? ctx.rooms : [];
2487
- const selectedIds = selected
2488
- .map((v) => (typeof v === 'string' ? v : v?.id))
2489
- .filter((v) => typeof v === 'string' && v.length > 0);
2490
- const includeRooms = rooms.filter((r) => selectedIds.includes(r.id));
2491
- await this.chatService.createTab({ name, title: ctx.title.trim(), includeRooms, excludeRooms: [] });
2492
- await this.loadTabs();
2493
- }
2494
- catch (err) {
2495
- console.error('Failed to create tab', err);
2496
- }
2497
- break;
2498
- }
2499
- case 'tab.remove': {
2500
- await this.deleteTab(this.contextTab);
2501
- break;
2502
- }
2503
- }
2504
- // (this.tab() as any).setIndicatorPosition(this.activeTab());
2505
- }
2506
- onContextItemClick(e) {
2507
- if (!this.contextRoom)
2508
- return;
2509
- const roomId = this.contextRoom.id;
2510
- switch (e.item.name) {
2511
- case 'open':
2512
- this.openChat(roomId);
2513
- break;
2514
- case 'mark-seen':
2515
- this.chatService
2516
- .markRoomSeen(roomId)
2517
- .catch(async () => this.toastService.danger(await this.translateService.translateAsync('@conversation:messages.mark-seen-failed')));
2518
- break;
2519
- // case 'mute':
2520
- // this.toastService.warning('Mute not implemented yet');
2521
- // break;
2522
- default: {
2523
- // Toggle tab inclusion: names are in the form 'tab.toggle:<tabId>'
2524
- if (typeof e.item.name === 'string' && e.item.name.startsWith('tab.toggle:')) {
2525
- const tabId = e.item.name.split(':')[1];
2526
- const currentTabs = this.tabs();
2527
- const foundTab = currentTabs.find((t) => t.id === tabId);
2528
- if (!foundTab) {
2529
- this.toastService.danger('@conversation:messages.tab-not-found');
2530
- return;
2531
- }
2532
- const isIncluded = (foundTab.includeRooms || []).some((r) => r.id === roomId);
2533
- const op = isIncluded
2534
- ? this.chatService.removeIncludeRoomFromTab(tabId, roomId)
2535
- : this.chatService.addIncludeRoomToTab(tabId, roomId);
2536
- op.then(async (updatedTab) => {
2537
- this.tabs.update((tabs) => tabs.map((t) => (t.id === updatedTab.id ? updatedTab : t)));
2538
- this.toastService.success(await this.translateService.translateAsync(isIncluded ? '@conversation:messages.tab-removed' : '@conversation:messages.tab-added'));
2539
- }).catch(async () => this.toastService.danger(await this.translateService.translateAsync('@conversation:messages.tab-update-failed')));
2540
- return;
2541
- }
2542
- break;
2543
- }
2544
- case 'tab.remove-current': {
2545
- const at = this.activeTab();
2546
- if (!at || at.name === 'all' || at.name === 'unread') {
2547
- return;
2548
- }
2549
- this.chatService
2550
- .removeIncludeRoomFromTab(at.id, roomId)
2551
- .then(async (updatedTab) => {
2552
- this.tabs.update((tabs) => tabs.map((t) => (t.id === updatedTab.id ? updatedTab : t)));
2553
- this.toastService.success(await this.translateService.translateAsync('@conversation:messages.tab-removed'));
2554
- })
2555
- .catch(async () => this.toastService.danger(await this.translateService.translateAsync('@conversation:messages.tab-remove-failed')));
2556
- break;
2557
- }
2558
- case 'delete-room':
2559
- this.deleteRoom(roomId);
2560
- break;
2561
- case 'leave-room': {
2562
- const currentUserId = this.sessionService.user?.id;
2563
- this.chatService
2564
- .removeParticipant(roomId, currentUserId)
2565
- .then(() => {
2566
- if (this.selectedRoom()?.id === roomId) {
2567
- this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2568
- }
2569
- })
2570
- .catch(async () => this.toastService.danger('Failed to leave'));
2571
- break;
2572
- }
2573
- }
2574
- }
2575
- onKeydownHandler(event) {
2576
- if (this.selectedRoom()) {
2577
- this.onBackButtonClick();
2578
- }
2579
- }
2580
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2581
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", host: { listeners: { "document:keydown.escape": "onKeydownHandler($event)" } }, providers: [
2582
- {
2583
- provide: AXPPageLayoutBase,
2584
- useExisting: AXMChatComponent,
2585
- },
2586
- AXUnsubscriber,
2587
- ], viewQueries: [{ propertyName: "tab", first: true, predicate: ["tab"], descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "chatContext", first: true, predicate: ["chatContext"], descendants: true, isSignal: true }, { propertyName: "tabContext", first: true, predicate: ["tabContext"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout #container>\n <axp-layout-start-side class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>{{ '@conversation:module-name' | translate | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"'@conversation:search.placeholder' | translate | async\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0 ax-max-w-80\">\n @if (!layoutService.isMobileDevice()) {\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content\n style=\"height: calc(100vh - 22rem)\"\n [style.padding]=\"layoutService.isMobileDevice() ? '1rem !important' : '0 !important'\"\n >\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"'@conversation:search.placeholder' | translate | async\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full ax-mb-2\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n<ng-template #template>\n <!-- Tabs -->\n <div [class]=\"!layoutService.isMobileDevice() ? 'ax-px-4' : ''\">\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400 !ax-overflow-x-hidden\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"activeTabIndex.set($event.index)\"\n >\n @for (tab of tabs(); track tab.id; let i = $index) {\n <ax-tab-item\n [text]=\"tab.title\"\n [active]=\"activeTabIndex() === i\"\n (contextmenu)=\"openTabContextMenu($event, tab, i)\"\n >\n @if (getRoomCountForTab(tab) > 0) {\n <ax-suffix>\n <ax-badge\n [text]=\"getRoomCountForTab(tab).toString()\"\n [color]=\"activeTabIndex() === i ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n\n <!-- Chat List Content -->\n <div\n class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between ax-min-w-80\"\n [class.ax-border-t]=\"tabs().length > 0\"\n >\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n } @else if (error()) {\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"'@conversation:chat-error'\"\n [description]=\"error() || '@conversation:state-message.error.description'\"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@conversation:try-again' | translate | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </axp-state-message>\n } @else {\n <div class=\"ax-flex-1 ax-overflow-y-auto ax-pt-1\">\n @for (i of filteredRooms(); track i.id) {\n @if (layoutService.isMobileDevice()) {\n <ax-sliding-item class=\"ax-h-20\">\n <ax-content class=\"!ax-p-0\">\n <axm-chat-item\n [data]=\"i\"\n [typing]=\"$any(typingStatus()[i.id])\"\n [lastMessageReaction]=\"i.lastMessage?.reactions?.[0]?.type ?? null\"\n [attr.data-id]=\"i.id\"\n (click)=\"openChat(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface ax-block\"\n [@fadeIn]\n tabindex=\"0\"\n (keydown.enter)=\"openChat(i.id)\"\n role=\"button\"\n (contextmenu)=\"openContextMenu($event, i)\"\n ></axm-chat-item>\n </ax-content>\n\n <!-- <ax-sliding-item-suffix (onClick)=\"onSlideAction(i, 'mute')\" color=\"warning\">\n <ax-icon class=\"fa-light fa-bell-slash fa-lg\"></ax-icon>\n </ax-sliding-item-suffix> -->\n <ax-sliding-item-suffix (onClick)=\"onSlideAction(i, 'delete')\" color=\"danger\">\n <ax-icon class=\"fa-light fa-trash-can fa-lg\"></ax-icon>\n </ax-sliding-item-suffix>\n </ax-sliding-item>\n } @else {\n <axm-chat-item\n [data]=\"i\"\n [typing]=\"$any(typingStatus()[i.id])\"\n [lastMessageReaction]=\"i.lastMessage?.reactions?.[0]?.type ?? null\"\n [attr.data-id]=\"i.id\"\n (click)=\"openChat(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\n [@fadeIn]\n tabindex=\"0\"\n (keydown.enter)=\"openChat(i.id)\"\n role=\"button\"\n (contextmenu)=\"openContextMenu($event, i)\"\n ></axm-chat-item>\n }\n } @empty {\n <axp-state-message\n icon=\"fa-light fa-comments\"\n [title]=\"'@conversation:no-chats-found.title'\"\n [description]=\"'@conversation:no-chats-found.description'\"\n >\n </axp-state-message>\n }\n </div>\n }\n\n <!-- Footer Content -->\n @if (!layoutService.isMobileDevice()) {\n <div class=\"ax-border-t ax-border-divider ax-bg-lightest ax-p-4\">\n <ax-button\n class=\"ax-w-full\"\n color=\"primary\"\n [text]=\"'@conversation:new-conversation.button' | translate | async\"\n (onClick)=\"onNewConversation()\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-plus\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n } @else {\n <ax-button\n class=\"ax-fixed ax-bottom-16 ax-right-8 !ax-rounded-full !ax-w-14 !ax-h-14 ax-shadow-lg\"\n color=\"primary\"\n (onClick)=\"onNewConversation()\"\n >\n <ax-icon class=\"ax-text-2xl\">\n <i class=\"fa-solid fa-plus\"></i>\n </ax-icon>\n </ax-button>\n }\n </div>\n</ng-template>\n\n<ax-context-menu\n #chatContext\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n [orientation]=\"'vertical'\"\n (onOpening)=\"onContextOpening($event)\"\n (onItemClick)=\"onContextItemClick($event)\"\n></ax-context-menu>\n\n<ax-context-menu\n #tabContext\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n [orientation]=\"'vertical'\"\n (onOpening)=\"onTabContextOpening($event)\"\n (onItemClick)=\"onTabContextItemClick($event)\"\n></ax-context-menu>\n", styles: [""], dependencies: [{ kind: "component", type: AXMChatItemComponent, selector: "axm-chat-item", inputs: ["data", "typing", "lastMessageReaction"], outputs: ["pressChatItem"] }, { kind: "ngmodule", type:
2588
- // Common Modules
2589
- CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type:
2590
- // Acorex Core Modules
2591
- AXFormatModule }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXCommentModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type:
2592
- // Acorex Component Modules
2593
- // AXResizableDirective,
2594
- AXImageModule }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXSlidingItemModule }, { kind: "component", type: i4$1.AXSlidingItemComponent, selector: "ax-sliding-item" }, { kind: "component", type: i4$1.AXSlidingItemSuffixComponent, selector: "ax-sliding-item-suffix", inputs: ["color", "text"], outputs: ["onClick"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i5.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: "ngmodule", type: AXTextBoxModule }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i6.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: "ngmodule", type: AXTabsModule }, { kind: "component", type: i7.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i7.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.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: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i9.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }, { kind: "component", type: AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "items", "target"], outputs: ["onItemClick", "onOpening"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], animations: [
2595
- trigger('fadeIn', [
2596
- transition(':enter', [
2597
- style({ opacity: 0, transform: 'translateY(10px)' }),
2598
- animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
2599
- ]),
2600
- ]),
2601
- ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1554
+ }),
1555
+ ], ngImport: i0, template: "<axp-page-layout #container axConversationContainer>\n <axp-layout-start-side class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header> </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0 ax-max-w-80\">\n <div class=\"ax-h-full ax-flex ax-flex-col ax-shrink-0 md:ax-flex\" [class.ax-hidden]=\"mobileView() === 'chat'\">\n <ax-conversation-sidebar></ax-conversation-sidebar>\n </div>\n </axp-layout-content>\n </axp-layout-start-side>\n <axp-page-header> </axp-page-header>\n\n <axp-page-content class=\"ax-h-full ax-overflow-hidden ax-p-0\">\n <ax-conversation-info-bar>\n <ax-prefix>\n <ax-button look=\"blank\" class=\"md:ax-hidden\" (onClick)=\"goBackToList()\">\n <ax-icon icon=\"fa-solid fa-arrow-left\"></ax-icon>\n </ax-button>\n </ax-prefix>\n </ax-conversation-info-bar>\n <ax-conversation-message-list class=\"ax-flex-1 ax-overflow-hidden\"></ax-conversation-message-list>\n </axp-page-content>\n\n <axp-page-footer class=\"ax-block\">\n <ax-conversation-composer></ax-conversation-composer>\n </axp-page-footer>\n</axp-page-layout>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: AXConversationContainerDirective, selector: "[axConversationContainer]" }, { kind: "component", type: AXSidebarComponent, selector: "ax-conversation-sidebar", outputs: ["conversationSelected"] }, { kind: "component", type: AXInfoBarComponent, selector: "ax-conversation-info-bar", outputs: ["avatarClick", "searchClick", "searchQuery", "menuItemAction"] }, { kind: "component", type: AXMessageListComponent, selector: "ax-conversation-message-list", inputs: ["avatarTemplate"], outputs: ["messageAction"] }, { kind: "component", type: AXComposerComponent, selector: "ax-conversation-composer", outputs: ["messageSent", "emojiClick", "attachClick", "voiceClick"] }, { 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: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.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: "ngmodule", type: AXTranslationModule }, { kind: "component", type:
1556
+ // AXP Layout
1557
+ AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2602
1558
  }
2603
1559
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatComponent, decorators: [{
2604
1560
  type: Component,
2605
- args: [{ selector: 'axm-chat', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2606
- {
2607
- provide: AXPPageLayoutBase,
2608
- useExisting: AXMChatComponent,
2609
- },
2610
- AXUnsubscriber,
2611
- ], imports: [
2612
- AXMChatItemComponent,
2613
- // Common Modules
1561
+ args: [{ selector: 'axm-chat', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2614
1562
  CommonModule,
2615
- FormsModule,
2616
- RouterModule,
2617
- // Acorex Core Modules
2618
- AXFormatModule,
2619
- AXConversationModule,
2620
- AXCommentModule,
2621
- AXTranslationModule,
2622
- // Acorex Component Modules
2623
- // AXResizableDirective,
2624
- AXImageModule,
2625
- AXAvatarModule,
2626
- AXBadgeModule,
2627
- AXSlidingItemModule,
1563
+ AXConversationContainerDirective,
1564
+ AXSidebarComponent,
1565
+ AXInfoBarComponent,
1566
+ AXMessageListComponent,
1567
+ AXComposerComponent,
1568
+ AXButtonComponent,
2628
1569
  AXDecoratorModule,
2629
- AXTextBoxModule,
2630
- AXSearchBoxModule,
2631
- AXTabsModule,
2632
- AXButtonModule,
2633
- AXLoadingModule,
2634
- AXWysiwygModule,
2635
- AXLabelModule,
2636
- AXSelectBoxModule,
2637
- AXFormModule,
2638
- AXDropdownButtonModule,
2639
- AXDropdownModule,
2640
- AXToolBarModule,
2641
- AXSkeletonModule,
1570
+ AXTranslationModule,
1571
+ // AXP Layout
2642
1572
  AXPPageLayoutComponent,
2643
1573
  AXPThemeLayoutBlockComponent,
2644
1574
  AXPThemeLayoutStartSideComponent,
2645
- AXPThemeLayoutHeaderComponent,
2646
- AXPThemeLayoutToolbarComponent,
2647
- AXContextMenuComponent,
2648
- AXPStateMessageComponent,
2649
- ], animations: [
2650
- trigger('fadeIn', [
2651
- transition(':enter', [
2652
- style({ opacity: 0, transform: 'translateY(10px)' }),
2653
- animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
2654
- ]),
2655
- ]),
2656
- ], template: "<axp-page-layout #container>\n <axp-layout-start-side class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>{{ '@conversation:module-name' | translate | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"'@conversation:search.placeholder' | translate | async\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0 ax-max-w-80\">\n @if (!layoutService.isMobileDevice()) {\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content\n style=\"height: calc(100vh - 22rem)\"\n [style.padding]=\"layoutService.isMobileDevice() ? '1rem !important' : '0 !important'\"\n >\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"'@conversation:search.placeholder' | translate | async\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full ax-mb-2\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n<ng-template #template>\n <!-- Tabs -->\n <div [class]=\"!layoutService.isMobileDevice() ? 'ax-px-4' : ''\">\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400 !ax-overflow-x-hidden\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"activeTabIndex.set($event.index)\"\n >\n @for (tab of tabs(); track tab.id; let i = $index) {\n <ax-tab-item\n [text]=\"tab.title\"\n [active]=\"activeTabIndex() === i\"\n (contextmenu)=\"openTabContextMenu($event, tab, i)\"\n >\n @if (getRoomCountForTab(tab) > 0) {\n <ax-suffix>\n <ax-badge\n [text]=\"getRoomCountForTab(tab).toString()\"\n [color]=\"activeTabIndex() === i ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n\n <!-- Chat List Content -->\n <div\n class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between ax-min-w-80\"\n [class.ax-border-t]=\"tabs().length > 0\"\n >\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n } @else if (error()) {\n <axp-state-message\n mode=\"error\"\n icon=\"fa-light fa-circle-exclamation\"\n [title]=\"'@conversation:chat-error'\"\n [description]=\"error() || '@conversation:state-message.error.description'\"\n >\n <ax-button\n slot=\"actions\"\n [text]=\"'@conversation:try-again' | translate | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </axp-state-message>\n } @else {\n <div class=\"ax-flex-1 ax-overflow-y-auto ax-pt-1\">\n @for (i of filteredRooms(); track i.id) {\n @if (layoutService.isMobileDevice()) {\n <ax-sliding-item class=\"ax-h-20\">\n <ax-content class=\"!ax-p-0\">\n <axm-chat-item\n [data]=\"i\"\n [typing]=\"$any(typingStatus()[i.id])\"\n [lastMessageReaction]=\"i.lastMessage?.reactions?.[0]?.type ?? null\"\n [attr.data-id]=\"i.id\"\n (click)=\"openChat(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface ax-block\"\n [@fadeIn]\n tabindex=\"0\"\n (keydown.enter)=\"openChat(i.id)\"\n role=\"button\"\n (contextmenu)=\"openContextMenu($event, i)\"\n ></axm-chat-item>\n </ax-content>\n\n <!-- <ax-sliding-item-suffix (onClick)=\"onSlideAction(i, 'mute')\" color=\"warning\">\n <ax-icon class=\"fa-light fa-bell-slash fa-lg\"></ax-icon>\n </ax-sliding-item-suffix> -->\n <ax-sliding-item-suffix (onClick)=\"onSlideAction(i, 'delete')\" color=\"danger\">\n <ax-icon class=\"fa-light fa-trash-can fa-lg\"></ax-icon>\n </ax-sliding-item-suffix>\n </ax-sliding-item>\n } @else {\n <axm-chat-item\n [data]=\"i\"\n [typing]=\"$any(typingStatus()[i.id])\"\n [lastMessageReaction]=\"i.lastMessage?.reactions?.[0]?.type ?? null\"\n [attr.data-id]=\"i.id\"\n (click)=\"openChat(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\n [@fadeIn]\n tabindex=\"0\"\n (keydown.enter)=\"openChat(i.id)\"\n role=\"button\"\n (contextmenu)=\"openContextMenu($event, i)\"\n ></axm-chat-item>\n }\n } @empty {\n <axp-state-message\n icon=\"fa-light fa-comments\"\n [title]=\"'@conversation:no-chats-found.title'\"\n [description]=\"'@conversation:no-chats-found.description'\"\n >\n </axp-state-message>\n }\n </div>\n }\n\n <!-- Footer Content -->\n @if (!layoutService.isMobileDevice()) {\n <div class=\"ax-border-t ax-border-divider ax-bg-lightest ax-p-4\">\n <ax-button\n class=\"ax-w-full\"\n color=\"primary\"\n [text]=\"'@conversation:new-conversation.button' | translate | async\"\n (onClick)=\"onNewConversation()\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-plus\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n } @else {\n <ax-button\n class=\"ax-fixed ax-bottom-16 ax-right-8 !ax-rounded-full !ax-w-14 !ax-h-14 ax-shadow-lg\"\n color=\"primary\"\n (onClick)=\"onNewConversation()\"\n >\n <ax-icon class=\"ax-text-2xl\">\n <i class=\"fa-solid fa-plus\"></i>\n </ax-icon>\n </ax-button>\n }\n </div>\n</ng-template>\n\n<ax-context-menu\n #chatContext\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n [orientation]=\"'vertical'\"\n (onOpening)=\"onContextOpening($event)\"\n (onItemClick)=\"onContextItemClick($event)\"\n></ax-context-menu>\n\n<ax-context-menu\n #tabContext\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n [orientation]=\"'vertical'\"\n (onOpening)=\"onTabContextOpening($event)\"\n (onItemClick)=\"onTabContextItemClick($event)\"\n></ax-context-menu>\n" }]
2657
- }], propDecorators: { tab: [{ type: i0.ViewChild, args: ['tab', { isSignal: true }] }], container: [{ type: i0.ViewChild, args: ['container', { isSignal: true }] }], chatContext: [{ type: i0.ViewChild, args: ['chatContext', { isSignal: true }] }], tabContext: [{ type: i0.ViewChild, args: ['tabContext', { isSignal: true }] }], onKeydownHandler: [{
2658
- type: HostListener,
2659
- args: ['document:keydown.escape', ['$event']]
2660
- }] } });
1575
+ AXPThemeLayoutHeaderComponent
1576
+ ], providers: [
1577
+ ...provideConversation({
1578
+ userApi: AXIndexedDBUserApi,
1579
+ conversationApi: AXIndexedDBConversationApi,
1580
+ messageApi: AXIndexedDBMessageAIApi,
1581
+ realtimeApi: AXIndexedDBRealtimeApi,
1582
+ config: {},
1583
+ registry: {
1584
+ messageRenderers: [
1585
+ AX_CONVERSATION_TEXT_RENDERER,
1586
+ AX_CONVERSATION_IMAGE_RENDERER,
1587
+ AX_CONVERSATION_VIDEO_RENDERER,
1588
+ AX_CONVERSATION_AUDIO_RENDERER,
1589
+ AX_CONVERSATION_VOICE_RENDERER,
1590
+ AX_CONVERSATION_FILE_RENDERER,
1591
+ AX_CONVERSATION_LOCATION_RENDERER,
1592
+ AX_CONVERSATION_STICKER_RENDERER,
1593
+ AX_CONVERSATION_CONTACT_RENDERER,
1594
+ ],
1595
+ messageActions: [
1596
+ AX_CONVERSATION_MESSAGE_REPLY_ACTION,
1597
+ AX_CONVERSATION_MESSAGE_FORWARD_ACTION,
1598
+ AX_CONVERSATION_MESSAGE_EDIT_ACTION,
1599
+ AX_CONVERSATION_MESSAGE_DELETE_ACTION,
1600
+ ],
1601
+ composerActions: [
1602
+ AX_CONVERSATION_COMPOSER_EMOJI_ACTION,
1603
+ AX_CONVERSATION_COMPOSER_IMAGE_ACTION,
1604
+ AX_CONVERSATION_COMPOSER_VIDEO_ACTION,
1605
+ AX_CONVERSATION_COMPOSER_FILE_ACTION,
1606
+ AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION,
1607
+ AX_CONVERSATION_COMPOSER_AUDIO_ACTION,
1608
+ AX_CONVERSATION_COMPOSER_CONTACT_ACTION,
1609
+ AX_CONVERSATION_COMPOSER_LOCATION_ACTION,
1610
+ ],
1611
+ composerTabs: [AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_STICKER_TAB],
1612
+ conversationTabs: [
1613
+ AX_CONVERSATION_TAB_ALL,
1614
+ AX_CONVERSATION_TAB_PRIVATE,
1615
+ AX_CONVERSATION_TAB_GROUPS,
1616
+ AX_CONVERSATION_TAB_CHANNELS,
1617
+ AX_CONVERSATION_TAB_UNREAD,
1618
+ AX_CONVERSATION_TAB_ARCHIVED,
1619
+ ],
1620
+ infoBarActions: [
1621
+ AX_CONVERSATION_INFO_BAR_SEARCH_ACTION,
1622
+ AX_CONVERSATION_INFO_BAR_INFO_ACTION,
1623
+ AX_CONVERSATION_INFO_BAR_MUTE_ACTION,
1624
+ AX_CONVERSATION_INFO_BAR_DIVIDER,
1625
+ AX_CONVERSATION_INFO_BAR_DELETE_ACTION,
1626
+ AX_CONVERSATION_INFO_BAR_BLOCK_ACTION,
1627
+ ],
1628
+ conversationItemActions: [
1629
+ AX_CONVERSATION_ITEM_MUTE_ACTION,
1630
+ AX_CONVERSATION_ITEM_PIN_ACTION,
1631
+ AX_CONVERSATION_ITEM_MARK_READ_ACTION,
1632
+ AX_CONVERSATION_ITEM_DIVIDER,
1633
+ AX_CONVERSATION_ITEM_DELETE_ACTION,
1634
+ AX_CONVERSATION_ITEM_BLOCK_ACTION,
1635
+ ],
1636
+ },
1637
+ }),
1638
+ ], template: "<axp-page-layout #container axConversationContainer>\n <axp-layout-start-side class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header> </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0 ax-max-w-80\">\n <div class=\"ax-h-full ax-flex ax-flex-col ax-shrink-0 md:ax-flex\" [class.ax-hidden]=\"mobileView() === 'chat'\">\n <ax-conversation-sidebar></ax-conversation-sidebar>\n </div>\n </axp-layout-content>\n </axp-layout-start-side>\n <axp-page-header> </axp-page-header>\n\n <axp-page-content class=\"ax-h-full ax-overflow-hidden ax-p-0\">\n <ax-conversation-info-bar>\n <ax-prefix>\n <ax-button look=\"blank\" class=\"md:ax-hidden\" (onClick)=\"goBackToList()\">\n <ax-icon icon=\"fa-solid fa-arrow-left\"></ax-icon>\n </ax-button>\n </ax-prefix>\n </ax-conversation-info-bar>\n <ax-conversation-message-list class=\"ax-flex-1 ax-overflow-hidden\"></ax-conversation-message-list>\n </axp-page-content>\n\n <axp-page-footer class=\"ax-block\">\n <ax-conversation-composer></ax-conversation-composer>\n </axp-page-footer>\n</axp-page-layout>\n" }]
1639
+ }], ctorParameters: () => [] });
2661
1640
 
2662
1641
  var chat_component = /*#__PURE__*/Object.freeze({
2663
1642
  __proto__: null,
2664
1643
  AXMChatComponent: AXMChatComponent
2665
1644
  });
2666
1645
 
2667
- /**
2668
- * Converts chat service response to chat preview format
2669
- * @param chatResponse - The response from chat service containing items and total
2670
- * @returns Array of formatted chat preview messages
2671
- */
2672
- function convertToChatPreview(currentUserId, chatResponse, userName) {
2673
- if (!chatResponse || !chatResponse.items?.length) {
2674
- return [];
2675
- }
2676
- // Map from AXMChatMessage to ChatPreviewMessage
2677
- return chatResponse.items.map((message) => {
2678
- // Determine message type based on content type
2679
- let messageType = 'text';
2680
- // Map contentType to preview type
2681
- if (message.message?.contentType) {
2682
- switch (message.message.contentType) {
2683
- case 'text':
2684
- messageType = 'text';
2685
- break;
2686
- case 'file':
2687
- messageType = 'file';
2688
- break;
2689
- case 'image':
2690
- messageType = 'image';
2691
- break;
2692
- case 'video':
2693
- messageType = 'video';
2694
- break;
2695
- case 'link':
2696
- // Determine if link is for audio or other content
2697
- const content = message.message.content || '';
2698
- if (content.includes('.mp3') || content.includes('.ogg') || content.includes('audio')) {
2699
- messageType = 'audio';
2700
- }
2701
- else {
2702
- messageType = 'text';
2703
- }
2704
- break;
2705
- default:
2706
- messageType = 'text';
2707
- }
2708
- }
2709
- // Extract author name if available
2710
- const name = message.author ? message.author.id : 'Unknown';
2711
- // Create the chat preview message
2712
- const previewMessage = {
2713
- id: message.id,
2714
- sendTime: new Date(message.auditInfo?.created?.at || new Date()),
2715
- readTime: new Date(), // Assuming "read" when converted
2716
- type: messageType,
2717
- content: message.message?.content || '',
2718
- name: name,
2719
- userName,
2720
- };
2721
- // Add fromId if the message is not from current user
2722
- if (currentUserId !== message.author.id) {
2723
- previewMessage.fromId = message.author?.id;
2724
- }
2725
- else {
2726
- previewMessage.showActionButton = true;
2727
- }
2728
- // Note: Reply information would need to be looked up from the actual messages
2729
- // This would typically be done by the service that has access to all messages
2730
- return previewMessage;
2731
- });
2732
- }
2733
- /**
2734
- * Creates a ChatPreviewMessage from reply information
2735
- * @param message - The original message that is being referenced
2736
- * @returns A ChatPreviewMessage formatted for use as a replyTo
2737
- */
2738
- function createReplyPreview(currentUserId, message, userName) {
2739
- if (!message)
2740
- return undefined;
2741
- return {
2742
- id: message.id,
2743
- sendTime: new Date(message.auditInfo?.created?.at || new Date()),
2744
- readTime: new Date(),
2745
- type: message.message?.contentType === 'image'
2746
- ? 'image'
2747
- : message.message?.contentType === 'video'
2748
- ? 'video'
2749
- : message.message?.contentType === 'file'
2750
- ? 'file'
2751
- : 'text',
2752
- content: message.message?.content || '',
2753
- name: message.author?.id || 'Unknown',
2754
- fromId: currentUserId !== message.author.id ? message.author?.id : undefined,
2755
- showActionButton: true,
2756
- userName,
2757
- };
2758
- }
2759
- /**
2760
- * Generate demo chat preview data with a variety of message types
2761
- * @returns Array of sample chat preview messages
2762
- */
2763
- function generateDemoChatPreview() {
2764
- return [
2765
- {
2766
- id: '0',
2767
- sendTime: new Date(),
2768
- type: 'text',
2769
- readTime: new Date(),
2770
- content: 'Hello John, How are you?',
2771
- name: 'test name',
2772
- },
2773
- {
2774
- id: '1',
2775
- fromId: '10',
2776
- sendTime: new Date(),
2777
- readTime: new Date(),
2778
- type: 'text',
2779
- content: 'Hello',
2780
- name: 'test name',
2781
- replyTo: {
2782
- id: '0',
2783
- sendTime: new Date(),
2784
- type: 'text',
2785
- readTime: new Date(),
2786
- content: 'Hello John, How are you?',
2787
- name: 'test name',
2788
- },
2789
- },
2790
- {
2791
- id: '2',
2792
- fromId: '10',
2793
- sendTime: new Date(),
2794
- readTime: new Date(),
2795
- type: 'voice',
2796
- name: 'test name',
2797
- content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
2798
- },
2799
- {
2800
- id: '3',
2801
- sendTime: new Date(),
2802
- readTime: new Date(),
2803
- type: 'text',
2804
- content: 'Can i have your address information?',
2805
- name: 'test name',
2806
- replyTo: {
2807
- id: '1',
2808
- fromId: '10',
2809
- sendTime: new Date(),
2810
- readTime: new Date(),
2811
- type: 'text',
2812
- content: 'Hello',
2813
- name: 'test name',
2814
- },
2815
- },
2816
- {
2817
- id: '4',
2818
- fromId: '10',
2819
- sendTime: new Date(),
2820
- readTime: new Date(),
2821
- type: 'file',
2822
- name: 'test name',
2823
- content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
2824
- },
2825
- {
2826
- id: '5',
2827
- sendTime: new Date(),
2828
- readTime: new Date(),
2829
- type: 'image',
2830
- name: 'test name',
2831
- content: `https://picsum.photos/300/200`,
2832
- },
2833
- {
2834
- id: '6',
2835
- sendTime: new Date(),
2836
- readTime: new Date(),
2837
- type: 'video',
2838
- name: 'test name',
2839
- content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
2840
- },
2841
- {
2842
- id: '7',
2843
- sendTime: new Date(),
2844
- type: 'text',
2845
- readTime: new Date(),
2846
- content: 'Hello John, How are you?',
2847
- name: 'test name',
2848
- },
2849
- {
2850
- id: '8',
2851
- fromId: '10',
2852
- sendTime: new Date(),
2853
- readTime: new Date(),
2854
- type: 'audio',
2855
- name: 'kids',
2856
- content: `https://actions.google.com/sounds/v1/ambiences/kids_playing.ogg`,
2857
- },
2858
- {
2859
- id: '9',
2860
- fromId: '10',
2861
- sendTime: new Date(),
2862
- readTime: new Date(),
2863
- type: 'image',
2864
- name: 'test name',
2865
- content: `https://picsum.photos/300/200`,
2866
- },
2867
- {
2868
- id: '10',
2869
- fromId: '10',
2870
- sendTime: new Date(),
2871
- readTime: new Date(),
2872
- type: 'image',
2873
- name: 'test name',
2874
- content: `https://picsum.photos/200/300`,
2875
- },
2876
- {
2877
- id: '11',
2878
- fromId: '10',
2879
- sendTime: new Date(),
2880
- readTime: new Date(),
2881
- type: 'video',
2882
- name: 'test name',
2883
- content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
2884
- },
2885
- {
2886
- id: '12',
2887
- sendTime: new Date(),
2888
- readTime: new Date(),
2889
- type: 'file',
2890
- name: 'test name',
2891
- content: `data:audio/webm;codecs=opus;base64,GkXfo59ChoE+u5BxHVL7ZAS1EF34fJ`,
2892
- },
2893
- {
2894
- id: '13',
2895
- sendTime: new Date(),
2896
- readTime: new Date(),
2897
- type: 'audio',
2898
- name: 'alarm',
2899
- content: `https://actions.google.com/sounds/v1/alarms/digital_watch_alarm_long.ogg`,
2900
- },
2901
- ];
2902
- }
2903
- /**
2904
- * Usage Examples:
2905
- *
2906
- * Example 1: Converting chat service response to chat preview format
2907
- * ```typescript
2908
- * import { AXMChatService } from '../chat.service';
2909
- * import { convertToChatPreview } from './chat-preview.helper';
2910
- *
2911
- * export class ChatPreviewComponent {
2912
- * constructor(private chatService: AXMChatService) {}
2913
- *
2914
- * async loadChatMessages(roomId: string) {
2915
- * const response = await this.chatService.getChatMessages(roomId);
2916
- *
2917
- * // Convert to chat preview format
2918
- * const previewMessages = convertToChatPreview(response);
2919
- *
2920
- * // Use the converted messages
2921
- * this.conversationService.chats.set(previewMessages);
2922
- * }
2923
- * }
2924
- * ```
2925
- *
2926
- * Example 2: Using demo data for development/testing
2927
- * ```typescript
2928
- * import { generateDemoChatPreview } from './chat-preview.helper';
2929
- *
2930
- * export class ChatPreviewDemoComponent {
2931
- * ngOnInit() {
2932
- * // Load demo messages
2933
- * const demoMessages = generateDemoChatPreview();
2934
- *
2935
- * // Use the demo messages
2936
- * this.conversationService.chats.set(demoMessages);
2937
- * }
2938
- * }
2939
- */
2940
-
2941
- class AXMChatPreviewComponent {
2942
- constructor() {
2943
- this.activatedRoute = inject(ActivatedRoute);
2944
- this.fileService = inject(AXFileService);
2945
- this.chatService = inject(AXMChatManagementService);
2946
- this.realtimeService = inject(AXMChatRealtimeService);
2947
- this.sessionService = inject(AXPSessionService);
2948
- this.toastService = inject(AXToastService);
2949
- this.unsubscribe = inject(AXUnsubscriber);
2950
- this.inputRef = viewChild(AXConversationInputComponent, ...(ngDevMode ? [{ debugName: "inputRef" }] : []));
2951
- this.activeMessage = signal(null, ...(ngDevMode ? [{ debugName: "activeMessage" }] : []));
2952
- this.typing$ = new Subject();
2953
- this.chatData = signal([], ...(ngDevMode ? [{ debugName: "chatData" }] : []));
2954
- this.room = signal(null, ...(ngDevMode ? [{ debugName: "room" }] : []));
2955
- this.isEditing = signal(false, ...(ngDevMode ? [{ debugName: "isEditing" }] : []));
2956
- this.editId = signal(null, ...(ngDevMode ? [{ debugName: "editId" }] : []));
2957
- this.height = signal(0, ...(ngDevMode ? [{ debugName: "height" }] : []));
2958
- this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2959
- this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
2960
- this.isChannel = computed(() => this.room()?.topic === 'channel', ...(ngDevMode ? [{ debugName: "isChannel" }] : []));
2961
- this.options = signal({
2962
- disabled: false,
2963
- readonly: false,
2964
- value: '',
2965
- }, ...(ngDevMode ? [{ debugName: "options" }] : []));
2966
- this.roomId = '';
2967
- //GO TO LAST MESSAGE EFFECT.
2968
- this.#af = afterNextRender(() => {
2969
- setTimeout(() => {
2970
- const div = document.querySelector('ax-conversation-view > div');
2971
- if (div) {
2972
- div.scrollTo({ top: div.scrollHeight, behavior: 'smooth' });
2973
- }
2974
- }, 500);
2975
- });
2976
- }
2977
- setHeight(height) {
2978
- this.height.set(height);
2979
- }
2980
- ngOnInit() {
2981
- this.activatedRoute.params.subscribe((params) => {
2982
- this.roomId = params['id'];
2983
- if (this.roomId) {
2984
- this.loadMessages(this.roomId);
2985
- this.loadRoomInfo(this.roomId);
2986
- }
2987
- });
2988
- this.realtimeService.message$.pipe(this.unsubscribe.takeUntilDestroy).subscribe(async (event) => {
2989
- if (event.type === 'add') {
2990
- const message = event.payload;
2991
- if (message.author.id !== this.sessionService.user?.id && message.roomId === this.roomId) {
2992
- await this.chatService.markSeen(message.id);
2993
- this.chatData.update((values) => {
2994
- const newMessage = convertToChatPreview(this.sessionService.user?.id || '', {
2995
- items: [message],
2996
- total: 1,
2997
- })[0];
2998
- return [...values, newMessage];
2999
- });
3000
- }
3001
- }
3002
- else if (event.type === 'remove') {
3003
- this.chatData.update((values) => values.filter((item) => item.id !== event.payload));
3004
- }
3005
- else if (event.type === 'update') {
3006
- const message = event.payload;
3007
- const convertedNewMessage = convertToChatPreview(this.sessionService.user?.id || '', {
3008
- items: [message],
3009
- total: 1,
3010
- })[0];
3011
- this.chatData.update((values) => values.map((item) => (item.id === message.id ? { ...convertedNewMessage, ...item } : item)));
3012
- }
3013
- });
3014
- this.typingSubscription = this.typing$.pipe(debounceTime(1000)).subscribe(() => {
3015
- if (this.roomId) {
3016
- this.chatService.stopTyping(this.roomId);
3017
- }
3018
- });
3019
- }
3020
- //GO TO LAST MESSAGE EFFECT.
3021
- #af;
3022
- ngOnDestroy() {
3023
- this.typingSubscription?.unsubscribe();
3024
- }
3025
- handleTyping(event) {
3026
- if (this.roomId) {
3027
- this.chatService.startTyping(this.roomId);
3028
- this.typing$.next();
3029
- }
3030
- }
3031
- handleFileChange(event) {
3032
- console.log('File Changed:', event);
3033
- }
3034
- handleCancelRecord(event) {
3035
- console.log('Recording Cancelled:', event);
3036
- }
3037
- handleEndRecord(event) {
3038
- this.fileService.blobToBase64(event.data.value).then((base64Content) => {
3039
- // Create a properly formatted chat preview message for voice
3040
- const newVoiceMessage = {
3041
- id: `${Math.floor(Math.random() * 10000)}`,
3042
- content: base64Content,
3043
- sendTime: new Date(),
3044
- readTime: new Date(),
3045
- type: 'voice',
3046
- name: 'test name',
3047
- };
3048
- this.chatData.update((values) => [...values, newVoiceMessage]);
3049
- });
3050
- }
3051
- handleOnSend(e) {
3052
- if (this.isEditing() && this.editId()) {
3053
- this.chatService
3054
- .editMessage(this.editId(), e.data.value, e.data.type || 'text')
3055
- .then((newMessage) => {
3056
- const convertedNewMessage = convertToChatPreview(this.sessionService.user?.id || '', {
3057
- items: [newMessage],
3058
- total: 1,
3059
- })[0];
3060
- this.chatData.update((values) => values.map((item) => (item.id === this.editId() ? { ...convertedNewMessage } : item)));
3061
- this.editId.set(null);
3062
- this.options.update((prev) => ({ ...prev, value: '' }));
3063
- this.isEditing.set(false);
3064
- })
3065
- .catch((error) => {
3066
- this.toastService.danger('Failed to edit message');
3067
- });
3068
- }
3069
- else {
3070
- if (e.data.value && this.roomId) {
3071
- const replyId = e.data.replyChat?.id;
3072
- this.chatService
3073
- .sendMessage(this.roomId, e.data.value, e.data.type || 'text', replyId)
3074
- .then((newMessage) => {
3075
- const convertedNewMessage = convertToChatPreview(this.sessionService.user?.id || '', {
3076
- items: [newMessage],
3077
- total: 1,
3078
- })[0];
3079
- this.chatData.update((values) => [...values, { ...convertedNewMessage, replyTo: replyId }]);
3080
- this.options.update((prev) => ({ ...prev, value: '' }));
3081
- this.scrollToEnd();
3082
- })
3083
- .catch((error) => {
3084
- this.toastService.danger('Failed to send message');
3085
- });
3086
- }
3087
- }
3088
- }
3089
- handleEnter() {
3090
- if (this.options().value && this.roomId) {
3091
- this.handleOnSend({ data: { value: this.options().value } });
3092
- }
3093
- }
3094
- handleOnAction(e) {
3095
- console.log('Action Triggered:', e);
3096
- }
3097
- scrollToEnd() {
3098
- const conversationView = document.querySelector('ax-conversation-view');
3099
- if (conversationView) {
3100
- conversationView.scrollTo({
3101
- top: conversationView.scrollHeight,
3102
- behavior: 'smooth',
3103
- });
3104
- }
3105
- }
3106
- async loadMessages(roomId) {
3107
- this.isLoading.set(true);
3108
- this.error.set(null);
3109
- try {
3110
- // Get messages from the chat service
3111
- const messageResponse = await this.chatService.getMessages(roomId);
3112
- // Convert to chat preview format
3113
- const chatPreviewMessages = convertToChatPreview(this.sessionService.user?.id || '', messageResponse).sort((a, b) => {
3114
- return new Date(a.sendTime).getTime() - new Date(b.sendTime).getTime();
3115
- });
3116
- // Set the messages in the conversation service
3117
- this.chatData.set(chatPreviewMessages);
3118
- }
3119
- catch (error) {
3120
- this.error.set('Failed to load messages. Please try again.');
3121
- console.error('Failed to load messages:', error);
3122
- }
3123
- finally {
3124
- this.isLoading.set(false);
3125
- }
3126
- }
3127
- async loadRoomInfo(roomId) {
3128
- try {
3129
- const roomData = await this.chatService.getRoom(roomId);
3130
- this.room.set(roomData);
3131
- }
3132
- catch (error) {
3133
- console.error('Failed to load room info:', error);
3134
- }
3135
- }
3136
- addItemHandler(e) {
3137
- this.activeMessage.set(e.data);
3138
- if (e.data.fromId) {
3139
- e.canceled = true;
3140
- return;
3141
- }
3142
- const userItem = [];
3143
- if (e.data.type === 'text') {
3144
- userItem.push({
3145
- text: 'Edit',
3146
- color: 'primary',
3147
- icon: 'fa-solid fa-pencil',
3148
- onClick: (id) => {
3149
- try {
3150
- this.isEditing.set(true);
3151
- this.editId.set(e.data.id);
3152
- this.options.update((prev) => ({ ...prev, value: e.data.content }));
3153
- this.addInputOverlay('edit', e.data);
3154
- }
3155
- catch (error) {
3156
- this.toastService.danger('Failed to edit message');
3157
- }
3158
- },
3159
- });
3160
- }
3161
- if (e.data.type === 'text') {
3162
- userItem.push({
3163
- text: 'Delete',
3164
- color: 'danger',
3165
- icon: 'fa-solid fa-trash-can',
3166
- onClick: (id) => {
3167
- try {
3168
- this.chatService.deleteMessage(id).then((res) => {
3169
- this.chatData.update((values) => values.filter((item) => item.id !== id));
3170
- if (!res) {
3171
- this.toastService.danger('Failed to delete message');
3172
- }
3173
- });
3174
- }
3175
- catch (error) {
3176
- this.toastService.danger('Failed to delete message');
3177
- }
3178
- },
3179
- });
3180
- }
3181
- e.items.push(...userItem);
3182
- }
3183
- addInputOverlay(type, message) {
3184
- const input = this.inputRef();
3185
- const icon = type === 'reply' ? 'fa-solid fa-reply' : 'fa-solid fa-pencil';
3186
- if (input) {
3187
- input.setActionBoxContainer(icon, message?.content || this.activeMessage()?.content || '');
3188
- }
3189
- }
3190
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3191
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXMChatPreviewComponent, isStandalone: true, selector: "axm-chat-preview", viewQueries: [{ propertyName: "inputRef", first: true, predicate: AXConversationInputComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n (onReplyClick)=\"addInputOverlay('reply', $event.data)\"\n [chatBoxHeight]=\"height() - (isChannel() ? 5 : 68) + 'px'\"\n ></ax-conversation-view>\n @if (!isChannel()) {\n <ax-conversation-input\n class=\"ax-px-2\"\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (ngModelChange)=\"handleTyping($event)\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n (onEnterPressed)=\"handleEnter()\"\n ></ax-conversation-input>\n }\n </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDomChangeDirective, selector: "[axDomChange]", outputs: ["axMutationObserver", "axResizeObserver"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "component", type: i2$2.AXConversationViewComponent, selector: "ax-conversation-view", inputs: ["chatBoxHeight", "isReplyArrowShown", "avatar"], outputs: ["onActionMenuOpening", "onAction", "onReplyClick", "onScrollEnd"] }, { kind: "component", type: i2$2.AXConversationInputComponent, selector: "ax-conversation-input", inputs: ["look", "placeholder", "maxLength", "hasAttachment", "hasVoice", "hasEmoji", "isLoading", "acceptFileType"], outputs: ["onActionClose", "onSendClick", "onStartRecording", "onCancelRecording", "onEnterPressed"] }, { kind: "component", type: i2$2.AXConversationContainerComponent, selector: "ax-conversation-container", inputs: ["chatData"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i3$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3192
- }
3193
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMChatPreviewComponent, decorators: [{
3194
- type: Component,
3195
- args: [{ selector: 'axm-chat-preview', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, AXDomChangeDirective, AXConversationModule, AXLoadingModule], template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n (onReplyClick)=\"addInputOverlay('reply', $event.data)\"\n [chatBoxHeight]=\"height() - (isChannel() ? 5 : 68) + 'px'\"\n ></ax-conversation-view>\n @if (!isChannel()) {\n <ax-conversation-input\n class=\"ax-px-2\"\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (ngModelChange)=\"handleTyping($event)\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n (onEnterPressed)=\"handleEnter()\"\n ></ax-conversation-input>\n }\n </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
3196
- }], propDecorators: { inputRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXConversationInputComponent), { isSignal: true }] }] } });
3197
-
3198
- var chatPreview_component = /*#__PURE__*/Object.freeze({
3199
- __proto__: null,
3200
- AXMChatPreviewComponent: AXMChatPreviewComponent
3201
- });
3202
-
3203
1646
  class AXMCommentRealtimeService {
3204
1647
  }
3205
1648
  class AXMCommentRealtimeServiceImpl extends AXMCommentRealtimeService {
@@ -3951,7 +2394,7 @@ class AXMCommentComponent {
3951
2394
  }
3952
2395
  }
3953
2396
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMCommentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3954
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXMCommentComponent, isStandalone: true, selector: "axm-comment", inputs: { refrenceType: { classPropertyName: "refrenceType", publicName: "refrenceType", isSignal: true, isRequired: true, transformFunction: null }, refrenceId: { classPropertyName: "refrenceId", publicName: "refrenceId", isSignal: true, isRequired: true, transformFunction: null }, subject: { classPropertyName: "subject", publicName: "subject", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-size-full ax-flex ax-flex-col ax-justify-between ax-p-4 ax-overflow-hidden\">\n <ax-comment-container class=\"ax-overflow-auto\">\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item\n [id]=\"comment.id!\"\n [replyCount]=\"comment.replyCount\"\n [class.highlighted]=\"highlightedCommentId() === comment.id\"\n [@fadeIn]\n >\n <axp-user-avatar #user [size]=\"48\" [userId]=\"comment.author.id\" ngProjectAs=\"'ax-avatar'\"></axp-user-avatar>\n <ax-title>{{ user.firstName() + ' ' + user.lastName() }}</ax-title>\n <ax-comment-date>{{ comment.auditInfo?.created?.at! | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [like]=\"isLikedByUser(comment)\">\n {{ getReactionsCount(comment) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n ></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\" [class.highlighted]=\"highlightedCommentId() === reply.id\" [@fadeIn]>\n <axp-user-avatar\n #user2\n [size]=\"48\"\n [userId]=\"reply.author.id\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title>{{ user2.firstName() + ' ' + user2.lastName() }}</ax-title>\n <ax-comment-date>{{ reply.auditInfo?.created?.at! | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(reply.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(reply)\" [like]=\"isLikedByUser(reply)\">\n {{ getReactionsCount(reply) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n ></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <div>\n @if (isReplyingMode() || isEditingMode()) {\n <div\n class=\"ax-flex ax-justify-between ax-rounded-b-none ax-border ax-border-b-0 ax-rounded-lg ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\"\n >\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i\n [class]=\"isReplyingMode() ? 'fa-reply' : 'fa-pen'\"\n class=\"fa-solid ax-text-primary-500 dark:ax-text-primary-300 ax-text-2xl ax-me-4\"\n ></i>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\">\n <p class=\"ax-text-primary-500 dark:ax-text-primary-300\">\n {{ isReplyingMode() ? 'Reply to ' : 'Edit Message' }}\n <span class=\"ax-font-bold\">\n {{ isReplyingMode() ? activeReplyComment()?.userName : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-wysiwyg-toolbar>\n <ax-prefix>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-prefix>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown() || !this.commentContent()\"\n type=\"submit\"\n color=\"primary\"\n mode=\"split\"\n text=\"Send\"\n (onClick)=\"submitComment()\"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n <!-- <ax-button-item-list>\n <ax-button-item (click)=\"submitComment()\" text=\"Send Private ...\" name=\"private\" data=\"private\">\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-user-secret\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list> -->\n </ax-dropdown-button>\n </ax-suffix>\n </ax-wysiwyg-toolbar>\n </ax-wysiwyg-container>\n </div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%}ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}ax-wysiwyg-container .highlighted{background:var(--ax-color-primary-050)!important;border-radius:.25rem;transition:background 1s ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1$2.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "readonly", "disabled", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i1$2.AXWysiwygViewComponent, selector: "ax-wysiwyg-view" }, { kind: "component", type: i1$2.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1$2.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1$2.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1$2.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1$2.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "component", type: i1$2.AXWysiwygToolbarComponent, selector: "ax-wysiwyg-toolbar" }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i9.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.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: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4$2.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4$2.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4$2.AXCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4$2.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["like"], outputs: ["likeChange"] }, { kind: "component", type: i4$2.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4$2.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4$2.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.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: i5$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i3$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i7$1.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i9$1.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }, { kind: "pipe", type: i10.AXFormatPipe, name: "format" }, { kind: "pipe", type: AsyncPipe, name: "async" }], animations: [
2397
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXMCommentComponent, isStandalone: true, selector: "axm-comment", inputs: { refrenceType: { classPropertyName: "refrenceType", publicName: "refrenceType", isSignal: true, isRequired: true, transformFunction: null }, refrenceId: { classPropertyName: "refrenceId", publicName: "refrenceId", isSignal: true, isRequired: true, transformFunction: null }, subject: { classPropertyName: "subject", publicName: "subject", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-size-full ax-flex ax-flex-col ax-justify-between ax-p-4 ax-overflow-hidden\">\n <ax-comment-container class=\"ax-overflow-auto\">\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item\n [id]=\"comment.id!\"\n [replyCount]=\"comment.replyCount\"\n [class.highlighted]=\"highlightedCommentId() === comment.id\"\n [@fadeIn]\n >\n <axp-user-avatar #user [size]=\"48\" [userId]=\"comment.author.id\" ngProjectAs=\"'ax-avatar'\"></axp-user-avatar>\n <ax-title>{{ user.firstName() + ' ' + user.lastName() }}</ax-title>\n <ax-comment-date>{{ comment.auditInfo?.created?.at! | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [like]=\"isLikedByUser(comment)\">\n {{ getReactionsCount(comment) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n ></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\" [class.highlighted]=\"highlightedCommentId() === reply.id\" [@fadeIn]>\n <axp-user-avatar\n #user2\n [size]=\"48\"\n [userId]=\"reply.author.id\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title>{{ user2.firstName() + ' ' + user2.lastName() }}</ax-title>\n <ax-comment-date>{{ reply.auditInfo?.created?.at! | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(reply.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(reply)\" [like]=\"isLikedByUser(reply)\">\n {{ getReactionsCount(reply) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n ></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <div>\n @if (isReplyingMode() || isEditingMode()) {\n <div\n class=\"ax-flex ax-justify-between ax-rounded-b-none ax-border ax-border-b-0 ax-rounded-lg ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\"\n >\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i\n [class]=\"isReplyingMode() ? 'fa-reply' : 'fa-pen'\"\n class=\"fa-solid ax-text-primary-500 dark:ax-text-primary-300 ax-text-2xl ax-me-4\"\n ></i>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\">\n <p class=\"ax-text-primary-500 dark:ax-text-primary-300\">\n {{ isReplyingMode() ? 'Reply to ' : 'Edit Message' }}\n <span class=\"ax-font-bold\">\n {{ isReplyingMode() ? activeReplyComment()?.userName : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-wysiwyg-toolbar>\n <ax-prefix>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-prefix>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown() || !this.commentContent()\"\n type=\"submit\"\n color=\"primary\"\n mode=\"split\"\n text=\"Send\"\n (onClick)=\"submitComment()\"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n <!-- <ax-button-item-list>\n <ax-button-item (click)=\"submitComment()\" text=\"Send Private ...\" name=\"private\" data=\"private\">\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-user-secret\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list> -->\n </ax-dropdown-button>\n </ax-suffix>\n </ax-wysiwyg-toolbar>\n </ax-wysiwyg-container>\n </div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%}ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}ax-wysiwyg-container .highlighted{background:var(--ax-color-primary-050)!important;border-radius:.25rem;transition:background 1s ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1$1.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "readonly", "disabled", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i1$1.AXWysiwygViewComponent, selector: "ax-wysiwyg-view" }, { kind: "component", type: i1$1.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1$1.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1$1.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1$1.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1$1.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "component", type: i1$1.AXWysiwygToolbarComponent, selector: "ax-wysiwyg-toolbar" }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1.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: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4.AXCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["like"], outputs: ["likeChange"] }, { kind: "component", type: i4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5.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: i5.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i6.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i7.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i9.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }, { kind: "pipe", type: i10.AXFormatPipe, name: "format" }, { kind: "pipe", type: AsyncPipe, name: "async" }], animations: [
3955
2398
  trigger('fadeIn', [
3956
2399
  transition(':enter', [
3957
2400
  style({ opacity: 0, transform: 'translateY(10px)' }),
@@ -4007,9 +2450,9 @@ class AXMCommentPopupComponent extends AXBasePageComponent {
4007
2450
  // Angular
4008
2451
  CommonModule }, { kind: "ngmodule", type:
4009
2452
  // ACoreX
4010
- AXDecoratorModule }, { kind: "component", type: i5.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: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.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: "ngmodule", type: AXTranslationModule }, { kind: "component", type:
2453
+ AXDecoratorModule }, { kind: "component", type: i1.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: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5.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: "ngmodule", type: AXTranslationModule }, { kind: "component", type:
4011
2454
  // Comment
4012
- AXMCommentComponent, selector: "axm-comment", inputs: ["refrenceType", "refrenceId", "subject"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
2455
+ AXMCommentComponent, selector: "axm-comment", inputs: ["refrenceType", "refrenceId", "subject"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
4013
2456
  }
4014
2457
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMCommentPopupComponent, decorators: [{
4015
2458
  type: Component,
@@ -4369,7 +2812,7 @@ function routesFactory() {
4369
2812
  children: [
4370
2813
  {
4371
2814
  path: ':id',
4372
- loadComponent: () => Promise.resolve().then(function () { return chatPreview_component; }).then((c) => c.AXMChatPreviewComponent),
2815
+ loadComponent: () => Promise.resolve().then(function () { return chat_component; }).then((c) => c.AXMChatComponent),
4373
2816
  },
4374
2817
  ],
4375
2818
  },
@@ -4415,7 +2858,7 @@ function routesFactory() {
4415
2858
  }
4416
2859
  class AXMConversationModule {
4417
2860
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMConversationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
4418
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXMConversationModule, imports: [i1$3.AXPWidgetCoreModule, i2$3.AXPWorkflowModule,
2861
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXMConversationModule, imports: [i1$2.AXPWidgetCoreModule, i2$1.AXPWorkflowModule,
4419
2862
  // Entity Modules
4420
2863
  AXMConversationTabEntityModule,
4421
2864
  AXMRoomEntityModule,
@@ -4599,5 +3042,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
4599
3042
  * Generated bundle index. Do not edit.
4600
3043
  */
4601
3044
 
4602
- export { AXMChatComponent, AXMChatItemComponent, AXMChatManagementService, AXMChatPreviewComponent, AXMChatRealtimeService, AXMChatRealtimeServiceImpl, AXMChatService, AXMChatServiceImpl, AXMCommentComponent, AXMCommentManagementService, AXMCommentPopupComponent, AXMCommentPopupStartAction, AXMCommentPopupWorkflow, AXMCommentRealtimeService, AXMCommentRealtimeServiceImpl, AXMCommentService, AXMCommentServiceImpl, AXMCommentWidgetViewComponent, AXMConversationModule, AXMConversationTabEntityModule, AXMConversationTabService, AXMConversationTabServiceImpl, AXMMessageEntityModule, AXMMessageService, AXMMessageServiceImpl, AXMRoomEntityModule, AXMRoomService, AXMRoomServiceImpl, AXPCommentWidget, RootConfig, messageFactory, roomFactory, tabFactory };
3045
+ export { AXMChatComponent, AXMChatManagementService, AXMChatRealtimeService, AXMChatRealtimeServiceImpl, AXMChatService, AXMChatServiceImpl, AXMCommentComponent, AXMCommentManagementService, AXMCommentPopupComponent, AXMCommentPopupStartAction, AXMCommentPopupWorkflow, AXMCommentRealtimeService, AXMCommentRealtimeServiceImpl, AXMCommentService, AXMCommentServiceImpl, AXMCommentWidgetViewComponent, AXMConversationModule, AXMConversationTabEntityModule, AXMConversationTabService, AXMConversationTabServiceImpl, AXMMessageEntityModule, AXMMessageService, AXMMessageServiceImpl, AXMRoomEntityModule, AXMRoomService, AXMRoomServiceImpl, AXPCommentWidget, RootConfig, messageFactory, roomFactory, tabFactory };
4603
3046
  //# sourceMappingURL=acorex-modules-conversation.mjs.map