@acorex/modules 19.4.4 → 19.4.5

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 (141) hide show
  1. package/conversation/README.md +113 -2
  2. package/conversation/index.d.ts +2 -14
  3. package/conversation/lib/const.d.ts +25 -6
  4. package/conversation/lib/conversation.module.d.ts +32 -3
  5. package/conversation/lib/entities/index.d.ts +2 -0
  6. package/conversation/lib/entities/message/index.d.ts +3 -0
  7. package/conversation/lib/entities/message/message.entity.d.ts +3 -0
  8. package/conversation/lib/entities/message/message.service.d.ts +10 -0
  9. package/conversation/lib/entities/message/message.types.d.ts +20 -0
  10. package/conversation/lib/entities/room/index.d.ts +3 -0
  11. package/conversation/lib/entities/room/room.entity.d.ts +3 -0
  12. package/conversation/lib/entities/room/room.service.d.ts +10 -0
  13. package/conversation/lib/entities/room/room.types.d.ts +8 -0
  14. package/conversation/lib/entity.provider.d.ts +12 -0
  15. package/conversation/lib/features/chat/chat-preview.helper.d.ts +68 -0
  16. package/conversation/lib/features/chat/chat.component.d.ts +42 -0
  17. package/conversation/lib/features/chat/chat.service.d.ts +101 -0
  18. package/conversation/lib/features/chat/chat.type.d.ts +38 -0
  19. package/conversation/lib/{entities → features}/chat/components/chat-item/chat-item.component.d.ts +6 -1
  20. package/conversation/lib/{entities → features}/chat/components/chat-preview/chat-preview.component.d.ts +3 -0
  21. package/conversation/lib/features/chat/index.d.ts +5 -0
  22. package/conversation/lib/{entities/comments/pages → features/comment}/comment-list-view.component.d.ts +12 -12
  23. package/conversation/lib/features/comment/comment.service.d.ts +66 -0
  24. package/conversation/lib/features/comment/comment.type.d.ts +45 -0
  25. package/conversation/lib/features/comment/index.d.ts +4 -0
  26. package/conversation/lib/features/index.d.ts +2 -0
  27. package/document-management/lib/entities/folder/folder.types.d.ts +3 -2
  28. package/document-management/lib/features/document-explorer/components/create-folder-dialog/create-folder-dialog.component.d.ts +5 -2
  29. package/document-management/lib/features/document-explorer/document-explorer.component.d.ts +22 -20
  30. package/document-management/lib/features/document-explorer/document-explorer.viewmodel.d.ts +22 -2
  31. package/document-management/lib/features/document-explorer/views/attachement-widget/attachment-widget.component.d.ts +52 -33
  32. package/document-management/lib/features/document-explorer/views/detail-panel/detail-panel.component.d.ts +52 -33
  33. package/document-management/lib/features/document-explorer/views/details/details-view.component.d.ts +52 -33
  34. package/document-management/lib/features/document-explorer/views/large-icons/large-icons-view.component.d.ts +52 -33
  35. package/document-management/lib/features/document-explorer/views/large-tiles/large-tiles-view.component.d.ts +52 -33
  36. package/document-management/lib/features/document-explorer/views/list/list-view.component.d.ts +52 -33
  37. package/document-management/lib/features/document-explorer/views/small-icons/small-icons-view.component.d.ts +52 -33
  38. package/document-management/lib/features/document-explorer/views/small-tiles/small-tiles-view.component.d.ts +52 -33
  39. package/document-management/lib/features/drive/drive.component.d.ts +52 -31
  40. package/document-management/lib/features/drive-choose/drive-choose.component.d.ts +49 -28
  41. package/document-management/lib/features/shared/components/document-type-choose-file/document-type-choose-file.component.d.ts +6 -6
  42. package/document-management/lib/features/shared/components/folder-path-breadcrumbs/folder-path-breadcrumbs.component.d.ts +21 -2
  43. package/document-management/lib/features/shared/document-manager.service.d.ts +177 -45
  44. package/document-management/lib/features/shared/document-manager.types.d.ts +2 -1
  45. package/document-management/lib/features/shared/index.d.ts +1 -0
  46. package/fesm2022/acorex-modules-common.mjs +7 -0
  47. package/fesm2022/acorex-modules-common.mjs.map +1 -1
  48. package/fesm2022/acorex-modules-conversation.mjs +2382 -820
  49. package/fesm2022/acorex-modules-conversation.mjs.map +1 -1
  50. package/fesm2022/{acorex-modules-document-management-acorex-modules-document-management-BpmG6Dyx.mjs → acorex-modules-document-management-acorex-modules-document-management-BATdoqJi.mjs} +1679 -2019
  51. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-BATdoqJi.mjs.map +1 -0
  52. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-DIF_t4fE.mjs → acorex-modules-document-management-attachment-widget.component-C1-gQepw.mjs} +2 -2
  53. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-DIF_t4fE.mjs.map → acorex-modules-document-management-attachment-widget.component-C1-gQepw.mjs.map} +1 -1
  54. package/fesm2022/acorex-modules-document-management-create-folder-dialog.component-ZMvsadWt.mjs +144 -0
  55. package/fesm2022/acorex-modules-document-management-create-folder-dialog.component-ZMvsadWt.mjs.map +1 -0
  56. package/fesm2022/{acorex-modules-document-management-details-view.component-D0RIYrHB.mjs → acorex-modules-document-management-details-view.component-CvHIETNf.mjs} +2 -2
  57. package/fesm2022/{acorex-modules-document-management-details-view.component-D0RIYrHB.mjs.map → acorex-modules-document-management-details-view.component-CvHIETNf.mjs.map} +1 -1
  58. package/fesm2022/{acorex-modules-document-management-drive-choose.component-DMAQr0nK.mjs → acorex-modules-document-management-drive-choose.component-UTy9OISj.mjs} +29 -9
  59. package/fesm2022/acorex-modules-document-management-drive-choose.component-UTy9OISj.mjs.map +1 -0
  60. package/fesm2022/acorex-modules-document-management-drive.component-Crh10Z5J.mjs +363 -0
  61. package/fesm2022/acorex-modules-document-management-drive.component-Crh10Z5J.mjs.map +1 -0
  62. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-C686Ec7s.mjs → acorex-modules-document-management-large-icons-view.component-BuV7MPG5.mjs} +2 -2
  63. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-C686Ec7s.mjs.map → acorex-modules-document-management-large-icons-view.component-BuV7MPG5.mjs.map} +1 -1
  64. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-0GpMVYv5.mjs → acorex-modules-document-management-large-tiles-view.component-CwYwVxoG.mjs} +2 -2
  65. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-0GpMVYv5.mjs.map → acorex-modules-document-management-large-tiles-view.component-CwYwVxoG.mjs.map} +1 -1
  66. package/fesm2022/{acorex-modules-document-management-list-view.component-C1inszTC.mjs → acorex-modules-document-management-list-view.component-BHEwRA3m.mjs} +2 -2
  67. package/fesm2022/{acorex-modules-document-management-list-view.component-C1inszTC.mjs.map → acorex-modules-document-management-list-view.component-BHEwRA3m.mjs.map} +1 -1
  68. package/fesm2022/{acorex-modules-document-management-permission-definition.provider-B7lgRLHi.mjs → acorex-modules-document-management-permission-definition.provider-B3qaYwRL.mjs} +2 -2
  69. package/fesm2022/{acorex-modules-document-management-permission-definition.provider-B7lgRLHi.mjs.map → acorex-modules-document-management-permission-definition.provider-B3qaYwRL.mjs.map} +1 -1
  70. package/fesm2022/{acorex-modules-document-management-rename-node-dialog.component-C9k8WgDG.mjs → acorex-modules-document-management-rename-node-dialog.component-CrcJm9jP.mjs} +5 -5
  71. package/fesm2022/{acorex-modules-document-management-rename-node-dialog.component-C9k8WgDG.mjs.map → acorex-modules-document-management-rename-node-dialog.component-CrcJm9jP.mjs.map} +1 -1
  72. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-BGcUzNCv.mjs → acorex-modules-document-management-small-icons-view.component-CD5UORaq.mjs} +2 -2
  73. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-BGcUzNCv.mjs.map → acorex-modules-document-management-small-icons-view.component-CD5UORaq.mjs.map} +1 -1
  74. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-CumumWvO.mjs → acorex-modules-document-management-small-tiles-view.component-cpc_xfbT.mjs} +2 -2
  75. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-CumumWvO.mjs.map → acorex-modules-document-management-small-tiles-view.component-cpc_xfbT.mjs.map} +1 -1
  76. package/fesm2022/acorex-modules-document-management.mjs +1 -1
  77. package/fesm2022/{acorex-modules-issue-management-acorex-modules-issue-management-ErtEMU89.mjs → acorex-modules-issue-management-acorex-modules-issue-management-DbVfrgVX.mjs} +5 -5
  78. package/fesm2022/acorex-modules-issue-management-acorex-modules-issue-management-DbVfrgVX.mjs.map +1 -0
  79. package/fesm2022/{acorex-modules-issue-management-capture-screen.component-B6k0Zn9C.mjs → acorex-modules-issue-management-capture-screen.component-_9mwJVkz.mjs} +2 -2
  80. package/fesm2022/{acorex-modules-issue-management-capture-screen.component-B6k0Zn9C.mjs.map → acorex-modules-issue-management-capture-screen.component-_9mwJVkz.mjs.map} +1 -1
  81. package/fesm2022/acorex-modules-issue-management.mjs +1 -1
  82. package/fesm2022/acorex-modules-notification-management.mjs +5 -5
  83. package/fesm2022/acorex-modules-notification-management.mjs.map +1 -1
  84. package/fesm2022/{acorex-modules-organization-management-org-chart.page-CP8zz-Bc.mjs → acorex-modules-organization-management-org-chart.page-CrOUUr4c.mjs} +1 -2
  85. package/fesm2022/acorex-modules-organization-management-org-chart.page-CrOUUr4c.mjs.map +1 -0
  86. package/fesm2022/acorex-modules-organization-management.mjs +2 -2
  87. package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-C9ZApxct.mjs → acorex-modules-platform-management-acorex-modules-platform-management-DtXOjIIK.mjs} +326 -87
  88. package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-DtXOjIIK.mjs.map +1 -0
  89. package/fesm2022/{acorex-modules-platform-management-list-version.component-D50Xundj.mjs → acorex-modules-platform-management-list-version.component-DY4yMd8n.mjs} +2 -2
  90. package/fesm2022/{acorex-modules-platform-management-list-version.component-D50Xundj.mjs.map → acorex-modules-platform-management-list-version.component-DY4yMd8n.mjs.map} +1 -1
  91. package/fesm2022/{acorex-modules-platform-management-settings.provider-DsHGn3AZ.mjs → acorex-modules-platform-management-settings.provider-BO-1dmVc.mjs} +2 -2
  92. package/fesm2022/{acorex-modules-platform-management-settings.provider-DsHGn3AZ.mjs.map → acorex-modules-platform-management-settings.provider-BO-1dmVc.mjs.map} +1 -1
  93. package/fesm2022/acorex-modules-platform-management.mjs +1 -1
  94. package/fesm2022/acorex-modules-project-management.mjs +108 -0
  95. package/fesm2022/acorex-modules-project-management.mjs.map +1 -1
  96. package/fesm2022/acorex-modules-settings-management.mjs +20 -21
  97. package/fesm2022/acorex-modules-settings-management.mjs.map +1 -1
  98. package/fesm2022/acorex-modules-workflow-management-task-board.page-3_Tn2GeA.mjs +305 -0
  99. package/fesm2022/acorex-modules-workflow-management-task-board.page-3_Tn2GeA.mjs.map +1 -0
  100. package/fesm2022/acorex-modules-workflow-management.mjs +123 -0
  101. package/fesm2022/acorex-modules-workflow-management.mjs.map +1 -0
  102. package/package.json +18 -14
  103. package/platform-management/lib/task-board.provider.d.ts +14 -0
  104. package/project-management/lib/task-board.provider.d.ts +14 -0
  105. package/settings-management/lib/settings-management.module.d.ts +1 -1
  106. package/workflow-management/README.md +3 -0
  107. package/workflow-management/index.d.ts +2 -0
  108. package/workflow-management/lib/const.d.ts +11 -0
  109. package/workflow-management/lib/features/task-board/index.d.ts +2 -0
  110. package/workflow-management/lib/features/task-board/settings.keys.d.ts +5 -0
  111. package/workflow-management/lib/features/task-board/task-board.module.d.ts +6 -0
  112. package/workflow-management/lib/features/task-board/task-board.page.d.ts +76 -0
  113. package/workflow-management/lib/features/task-board/task-board.service.d.ts +13 -0
  114. package/workflow-management/lib/features/task-board/task-board.viewmodel.d.ts +20 -0
  115. package/workflow-management/lib/features/task-board/views/task-board-calendar-view/task-board-calendar-view.page.d.ts +25 -0
  116. package/workflow-management/lib/workflow-management.module.d.ts +8 -0
  117. package/conversation/lib/entities/chat/chat.module.d.ts +0 -28
  118. package/conversation/lib/entities/chat/chat.service.d.ts +0 -14
  119. package/conversation/lib/entities/chat/chat.type.d.ts +0 -22
  120. package/conversation/lib/entities/chat/components/chat-item-footer/chat-item-footer.component.d.ts +0 -5
  121. package/conversation/lib/entities/chat/components/chat-item-header/chat-item-header.component.d.ts +0 -12
  122. package/conversation/lib/entities/chat/components/chat-preview-header/chat-preview-header.component.d.ts +0 -7
  123. package/conversation/lib/entities/chat/pages/chat/chat.component.d.ts +0 -23
  124. package/conversation/lib/entities/comments/comment.module.d.ts +0 -26
  125. package/conversation/lib/entities/comments/comments.service.d.ts +0 -13
  126. package/conversation/lib/entities/comments/comments.type.d.ts +0 -74
  127. package/document-management/lib/features/widgets/document-attachment/document-attachment-widget-column.component.d.ts +0 -6
  128. package/document-management/lib/features/widgets/document-attachment/document-attachment-widget-edit.component.d.ts +0 -212
  129. package/document-management/lib/features/widgets/document-attachment/document-attachment-widget-print.component.d.ts +0 -6
  130. package/document-management/lib/features/widgets/document-attachment/document-attachment-widget-view.component.d.ts +0 -6
  131. package/document-management/lib/features/widgets/document-attachment/document-attachment-widget.config.d.ts +0 -7
  132. package/document-management/lib/features/widgets/document-attachment/index.d.ts +0 -5
  133. package/document-management/lib/features/widgets/index.d.ts +0 -1
  134. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-BpmG6Dyx.mjs.map +0 -1
  135. package/fesm2022/acorex-modules-document-management-create-folder-dialog.component-CCvKUDsw.mjs +0 -141
  136. package/fesm2022/acorex-modules-document-management-create-folder-dialog.component-CCvKUDsw.mjs.map +0 -1
  137. package/fesm2022/acorex-modules-document-management-drive-choose.component-DMAQr0nK.mjs.map +0 -1
  138. package/fesm2022/acorex-modules-issue-management-acorex-modules-issue-management-ErtEMU89.mjs.map +0 -1
  139. package/fesm2022/acorex-modules-organization-management-org-chart.page-CP8zz-Bc.mjs.map +0 -1
  140. package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-C9ZApxct.mjs.map +0 -1
  141. /package/conversation/lib/{entities/comments/pages → features/comment}/comment-lookup-popup.component.d.ts +0 -0
@@ -1,1012 +1,2574 @@
1
- import * as i2$2 from '@acorex/components/avatar';
1
+ import * as i3$1 from '@acorex/cdk/resizable';
2
+ import { AXResizableDirective } from '@acorex/cdk/resizable';
3
+ import * as i2 from '@acorex/components/avatar';
2
4
  import { AXAvatarModule } from '@acorex/components/avatar';
3
- import * as i4 from '@acorex/components/button';
5
+ import * as i3 from '@acorex/components/badge';
6
+ import { AXBadgeModule } from '@acorex/components/badge';
7
+ import * as i6$1 from '@acorex/components/button';
4
8
  import { AXButtonModule } from '@acorex/components/button';
5
- import * as i13 from '@acorex/components/comment';
9
+ import * as i2$4 from '@acorex/components/comment';
6
10
  import { AXCommentModule } from '@acorex/components/comment';
7
- import * as i2 from '@acorex/components/decorators';
11
+ import * as i2$2 from '@acorex/components/conversation';
12
+ import { AXConversationService, AXConversationModule } from '@acorex/components/conversation';
13
+ import * as i5 from '@acorex/components/decorators';
8
14
  import { AXDecoratorModule } from '@acorex/components/decorators';
9
- import * as i10 from '@acorex/components/dropdown';
15
+ import * as i11$1 from '@acorex/components/dropdown';
10
16
  import { AXDropdownModule } from '@acorex/components/dropdown';
11
- import * as i9 from '@acorex/components/dropdown-button';
17
+ import * as i10$1 from '@acorex/components/dropdown-button';
12
18
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
13
- import * as i7 from '@acorex/components/form';
19
+ import * as i9 from '@acorex/components/form';
14
20
  import { AXFormModule } from '@acorex/components/form';
15
- import * as i1$1 from '@acorex/components/image';
21
+ import * as i1 from '@acorex/components/image';
16
22
  import { AXImageModule } from '@acorex/components/image';
17
23
  import { AXLabelModule } from '@acorex/components/label';
18
- import * as i5 from '@acorex/components/loading';
24
+ import * as i5$1 from '@acorex/components/loading';
19
25
  import { AXLoadingModule } from '@acorex/components/loading';
26
+ import * as i6 from '@acorex/components/search-box';
27
+ import { AXSearchBoxModule } from '@acorex/components/search-box';
20
28
  import { AXSelectBoxModule } from '@acorex/components/select-box';
21
- import * as i12 from '@acorex/components/skeleton';
29
+ import * as i13 from '@acorex/components/skeleton';
22
30
  import { AXSkeletonModule } from '@acorex/components/skeleton';
23
- import * as i3$1 from '@acorex/components/text-box';
31
+ import * as i7 from '@acorex/components/tabs';
32
+ import { AXTabsModule } from '@acorex/components/tabs';
24
33
  import { AXTextBoxModule } from '@acorex/components/text-box';
25
- import * as i11 from '@acorex/components/toolbar';
34
+ import * as i12 from '@acorex/components/toolbar';
26
35
  import { AXToolBarModule } from '@acorex/components/toolbar';
27
- import * as i3 from '@acorex/components/wysiwyg';
36
+ import * as i8 from '@acorex/components/wysiwyg';
28
37
  import { AXWysiwygModule } from '@acorex/components/wysiwyg';
29
38
  import * as i15 from '@acorex/core/format';
30
39
  import { AXFormatModule } from '@acorex/core/format';
40
+ import { AXTranslationModule } from '@acorex/core/translation';
41
+ import * as i11 from '@acorex/platform/layout/components';
42
+ import { AXPThemeLayoutBlockComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutToolbarComponent } from '@acorex/platform/layout/components';
43
+ import { AXMEntityCrudServiceImpl, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
44
+ import * as i10 from '@acorex/platform/layout/views';
45
+ import { AXPPageLayoutComponent } from '@acorex/platform/layout/views';
46
+ import { AXPRootLayoutComponent } from '@acorex/platform/themes/default';
31
47
  import * as i14 from '@angular/common';
32
48
  import { CommonModule } from '@angular/common';
33
49
  import * as i0 from '@angular/core';
34
- import { Injectable, Component, signal, viewChild, inject, computed, ChangeDetectionStrategy, NgModule, input, output, afterNextRender, ViewEncapsulation, importProvidersFrom } from '@angular/core';
35
- import * as i1 from '@angular/forms';
50
+ import { Injectable, inject, input, output, computed, ChangeDetectionStrategy, Component, viewChild, signal, afterNextRender, importProvidersFrom, NgModule } from '@angular/core';
51
+ import * as i1$1 from '@angular/forms';
36
52
  import { FormsModule } from '@angular/forms';
37
- import * as i1$3 from '@angular/router';
38
- import { ActivatedRoute, RouterModule, Router } from '@angular/router';
39
- import { AXMEntityCrudServiceImpl, AXPEntityDataProviderImpl } from '@acorex/platform/layout/entity';
53
+ import * as i2$1 from '@angular/router';
54
+ import { Router, ActivatedRoute, RouterModule } from '@angular/router';
55
+ import { createAllQueryView, AXPEntityCommandScope, AXPEntityQueryType } from '@acorex/platform/common';
56
+ import * as i2$3 from '@acorex/platform/layout/builder';
57
+ import { AXPWidgetsCatalog, AXPLayoutBuilderModule } from '@acorex/platform/layout/builder';
58
+ import { AXMUsersEntityService } from '@acorex/modules/security-management';
59
+ import { AXPSessionService } from '@acorex/platform/auth';
60
+ import { AXFileService } from '@acorex/core/file';
40
61
  import { AXDialogService } from '@acorex/components/dialog';
41
62
  import { AXPopupService } from '@acorex/components/popup';
42
63
  import { AXToastService } from '@acorex/components/toast';
43
64
  import { AXPlatform } from '@acorex/core/platform';
44
- import { AXBasePageComponent } from '@acorex/components/page';
45
- import * as i2$1 from '@acorex/platform/layout/builder';
46
- import { AXPLayoutBuilderModule } from '@acorex/platform/layout/builder';
47
65
  import { DomSanitizer } from '@angular/platform-browser';
48
- import * as i1$2 from '@acorex/components/badge';
49
- import { AXBadgeModule } from '@acorex/components/badge';
50
- import * as i2$3 from '@acorex/components/conversation';
51
- import { AXConversationService, AXConversationModule } from '@acorex/components/conversation';
52
- import * as i4$1 from '@acorex/components/tabs';
53
- import { AXTabsModule } from '@acorex/components/tabs';
54
- import { AXPRootLayoutComponent } from '@acorex/platform/themes/default';
55
- import { AXPSessionService } from '@acorex/platform/auth';
56
- import { AXFileService } from '@acorex/core/file';
66
+ import { AXBasePageComponent } from '@acorex/components/page';
57
67
 
58
- const AXMConverstionModuleConst = {
59
- moduleName: 'Converstion',
60
- moduleRoute: 'converstion',
61
- i18n: '#converstion',
62
- chatName: 'Chat',
63
- commentName: 'Comment',
68
+ const config = {
69
+ i18n: 'conversation',
70
+ module: 'Conversation',
71
+ };
72
+ const RootConfig = {
73
+ config,
74
+ module: {
75
+ route: 'conversation',
76
+ name: config.module,
77
+ title: `t('module-name', {scope:"${config.i18n}"})`,
78
+ icon: 'fa-light fa-comments',
79
+ },
80
+ entities: {
81
+ room: {
82
+ name: 'room',
83
+ title: `t("room.plural-title", { scope: "${config.i18n}" })`,
84
+ source: `${config.module}.Room`,
85
+ icon: 'fa-light fa-comments',
86
+ },
87
+ message: {
88
+ name: 'message',
89
+ title: `t("message.plural-title", { scope: "${config.i18n}" })`,
90
+ source: `${config.module}.Message`,
91
+ icon: 'fa-light fa-message',
92
+ },
93
+ },
64
94
  };
65
95
 
66
- class AXMCommentService extends AXMEntityCrudServiceImpl {
96
+ class AXMMessageService extends AXMEntityCrudServiceImpl {
67
97
  }
68
- class AXMCommentServiceImpl extends AXMEntityCrudServiceImpl {
98
+ class AXMMessageServiceImpl extends AXMMessageService {
69
99
  constructor() {
70
- super(`${AXMConverstionModuleConst.moduleName}.${AXMConverstionModuleConst.commentName}`);
71
- this.messageReactionDataProvider = new AXPEntityDataProviderImpl(super.storageService, 'messageReaction');
72
- }
73
- async like(payload) {
74
- return this.messageReactionDataProvider.insertOne(payload);
100
+ super(`${RootConfig.module.name}.${RootConfig.entities.message.name}`);
75
101
  }
76
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
77
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentServiceImpl }); }
102
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMMessageServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
103
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMMessageServiceImpl }); }
78
104
  }
79
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentServiceImpl, decorators: [{
105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMMessageServiceImpl, decorators: [{
80
106
  type: Injectable
81
107
  }], ctorParameters: () => [] });
82
108
 
83
- class AXMCommentLookupPopup extends AXBasePageComponent {
84
- constructor() {
85
- super(...arguments);
86
- this.lookupNode = {
87
- name: 'lookup',
88
- path: 'lookup',
89
- type: 'lookup-editor',
90
- options: {
91
- entity: 'axoidc.users',
109
+ async function messageFactory(injector) {
110
+ const dataService = injector.get(AXMMessageService);
111
+ const i18n = RootConfig.config.i18n;
112
+ const entityDef = {
113
+ module: RootConfig.module.name,
114
+ name: RootConfig.entities.message.name,
115
+ source: '',
116
+ title: RootConfig.entities.message.title,
117
+ formats: {
118
+ individual: RootConfig.entities.message.title,
119
+ plural: RootConfig.entities.message.title,
120
+ searchResult: {
121
+ title: '{{ title }}',
122
+ description: RootConfig.module.title,
92
123
  },
93
- };
94
- this.context = {};
95
- }
96
- handleClose() {
97
- this.close({
98
- result: true,
99
- data: this.context,
100
- });
101
- }
102
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentLookupPopup, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
103
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AXMCommentLookupPopup, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `<axp-widgets-container class="ax-flex ax-flex-col ax-gap-2 ax-p-4" [context]="context" (onContextChanged)="context=$event.data">
104
- <div class="ax-m-5">
105
- <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
106
- </div>
107
- </axp-widgets-container>
108
-
109
- <ax-footer>
110
- <ax-suffix>
111
- <ax-button text="Accept & Send" color="primary" (onClick)="handleClose()"></ax-button>
112
- </ax-suffix>
113
- </ax-footer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.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: AXPLayoutBuilderModule }, { kind: "component", type: i2$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i2$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i4.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"] }] }); }
114
- }
115
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentLookupPopup, decorators: [{
116
- type: Component,
117
- args: [{
118
- template: `<axp-widgets-container class="ax-flex ax-flex-col ax-gap-2 ax-p-4" [context]="context" (onContextChanged)="context=$event.data">
119
- <div class="ax-m-5">
120
- <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
121
- </div>
122
- </axp-widgets-container>
123
-
124
- <ax-footer>
125
- <ax-suffix>
126
- <ax-button text="Accept & Send" color="primary" (onClick)="handleClose()"></ax-button>
127
- </ax-suffix>
128
- </ax-footer>`,
129
- imports: [AXDecoratorModule, AXPLayoutBuilderModule, AXButtonModule]
130
- }]
131
- }] });
132
-
133
- class AXMCommentListViewComponent {
134
- constructor() {
135
- this.hasCooldown = signal(false);
136
- this.commentContent = signal('');
137
- this.isSubmitting = signal(false);
138
- this.isReplyingMode = signal(false);
139
- this.isEditingMode = signal(false);
140
- this.isLoading = signal(true);
141
- this.failedImageIds = signal([]);
142
- this.activeReplyComment = signal(undefined);
143
- this.activeEditComment = signal(undefined);
144
- this.wysiwygEditor = viewChild.required('w');
145
- this.commentService = inject(AXMCommentService);
146
- this.platform = inject(AXPlatform);
147
- this.route = inject(ActivatedRoute);
148
- this.popupService = inject(AXPopupService);
149
- this.toastService = inject(AXToastService);
150
- this.dialogService = inject(AXDialogService);
151
- this.sanitize = inject(DomSanitizer);
152
- this.routeParams = this.route.snapshot;
153
- this.getPayload = computed(() => ({
154
- params: this.payload(),
155
- skip: 0,
156
- take: 10,
157
- }));
158
- this.payload = computed(() => ({
159
- roomType: 'default',
160
- entityId: this.routeParams.params?.['module'] + '.' + this.routeParams.params?.['entity'],
161
- instanceId: this.routeParams.params?.['id'],
162
- }));
163
- this.comments = signal([]);
164
- this.wysiwyg = viewChild('w');
165
- this.wysiwygOptions = signal({
166
- look: 'solid',
167
- });
168
- this.avatarConfig = signal({
169
- color: 'primary',
170
- look: 'rounded',
171
- type: 'solid', // 'image' | 'text' | 'icon' | 'default'
172
- });
173
- this.validateContent = (content) => {
174
- let isValid = true;
175
- if (!content || content === '<p><br></p>') {
176
- isValid = false;
177
- }
178
- return {
179
- rule: 'callback',
180
- result: isValid,
181
- message: isValid ? '' : 'Please fill the content',
182
- value: content,
183
- };
184
- };
185
- }
186
- ngOnInit() {
187
- this.loadComments();
188
- }
189
- sanitizeHtml(htmlContent) {
190
- return this.sanitize.bypassSecurityTrustHtml(htmlContent);
191
- }
192
- handleImageError(imageId) {
193
- this.failedImageIds.update((ids) => [...ids, imageId]);
194
- }
195
- checkImageExists(imageId) {
196
- return !this.failedImageIds().includes(imageId);
197
- }
198
- extractInitials(name) {
199
- const words = name.split(' ');
200
- const initials = words.map((word) => word.charAt(0).toUpperCase());
201
- return initials.join('');
202
- }
203
- async loadComments() {
204
- this.isLoading.set(true);
205
- // const response = await this.commentService.get(this.payload());
206
- const response = await this.commentService.query(this.getPayload());
207
- this.comments.set(response.items);
208
- setTimeout(() => {
209
- this.isLoading.set(false);
210
- }, 250);
211
- }
212
- editMessage(comment, reply) {
213
- this.isReplyingMode.set(false);
214
- this.activeReplyComment.set(undefined);
215
- this.isEditingMode.set(true);
216
- this.activeEditComment.set(comment);
217
- const contentToEdit = reply ? reply.content : comment.content;
218
- this.commentContent.set(contentToEdit);
219
- document.getElementsByClassName('ql-editor')[0].innerHTML = contentToEdit;
220
- this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
221
- }
222
- replyMessage(comment, reply) {
223
- this.isEditingMode.set(false);
224
- this.activeEditComment.set(undefined);
225
- this.isReplyingMode.set(true);
226
- this.activeReplyComment.set(comment);
227
- if (reply) {
228
- const mention = `<a data-id="${reply.id}">@${reply.user?.userName}</a> `;
229
- this.commentContent.set(mention);
230
- this.wysiwyg()?.focus();
231
- document.getElementsByClassName('ql-editor')[0].innerHTML = mention;
232
- }
233
- this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
234
- }
235
- async deleteComment(comment) {
236
- const dialog = this.dialogService.open({
237
- icon: 'fa-regular fa-warning',
238
- content: 'Are you sure you want to delete this comment?',
239
- title: 'Delete Comment',
240
- type: 'danger',
241
- orientation: 'horizontal',
242
- buttons: [
243
- {
244
- text: 'Delete',
245
- color: 'danger',
246
- onClick: async (e) => {
247
- e.handled = true;
248
- e.source.text = 'Deleting...';
249
- e.source.disabled = true;
250
- e.source.loading = true;
251
- try {
252
- await this.commentService.deleteOne(comment.id);
253
- this.removeMessageById(comment.id);
254
- this.toastService.show({
255
- content: 'Comment deleted successfully.',
256
- color: 'success',
257
- location: 'bottom-center',
258
- closeButton: true,
259
- timeOut: 3000,
260
- timeOutProgress: true,
261
- });
262
- (this.isEditingMode() && this.activeEditComment()?.id === comment.id) || this.resetReplyEditState;
263
- dialog.close();
264
- }
265
- catch (error) {
266
- this.toastService.show({
267
- content: typeof error === 'string' ? error : 'Failed to delete comment!',
268
- color: 'danger',
269
- location: 'bottom-center',
270
- closeButton: true,
271
- timeOut: 3000,
272
- timeOutProgress: true,
273
- });
274
- }
124
+ },
125
+ relatedEntities: [],
126
+ groups: [
127
+ {
128
+ id: 'section',
129
+ title: RootConfig.entities.message.title,
130
+ },
131
+ ],
132
+ properties: [
133
+ {
134
+ name: 'name',
135
+ title: `t("name", { scope: "common" })`,
136
+ groupId: 'section',
137
+ options: {
138
+ sort: {
139
+ enabled: true,
275
140
  },
276
- },
277
- {
278
- text: 'Cancel',
279
- color: 'default',
280
- autofocus: true,
281
- onClick: (e) => {
282
- dialog.close();
141
+ filter: {
142
+ advance: {
143
+ enabled: true,
144
+ },
145
+ inline: {
146
+ enabled: false,
147
+ },
283
148
  },
284
149
  },
285
- ],
286
- closeButton: false,
287
- });
288
- }
289
- async deleteReply(comment, reply) {
290
- const dialog = this.dialogService.open({
291
- icon: 'fa-regular fa-warning',
292
- content: 'Are you sure you want to delete this reply?',
293
- title: 'Delete Reply',
294
- type: 'danger',
295
- orientation: 'horizontal',
296
- buttons: [
297
- {
298
- text: 'Delete',
299
- color: 'danger',
300
- onClick: async (e) => {
301
- e.handled = true;
302
- e.source.text = 'Deleting...';
303
- e.source.disabled = true;
304
- e.source.loading = true;
305
- try {
306
- await this.commentService.deleteOne(comment.id);
307
- this.removeMessageById(comment.id, reply.id);
308
- this.toastService.show({
309
- content: 'Comment deleted successfully.',
310
- color: 'success',
311
- location: 'bottom-center',
312
- closeButton: true,
313
- timeOut: 3000,
314
- timeOutProgress: true,
315
- });
316
- dialog.close();
317
- }
318
- catch (error) {
319
- this.toastService.show({
320
- content: typeof error === 'string' ? error : 'Failed to delete comment!',
321
- color: 'danger',
322
- location: 'bottom-center',
323
- closeButton: true,
324
- timeOut: 3000,
325
- timeOutProgress: true,
326
- });
327
- }
150
+ schema: {
151
+ dataType: 'string',
152
+ interface: {
153
+ type: AXPWidgetsCatalog.text,
328
154
  },
329
155
  },
330
- {
331
- text: 'Cancel',
332
- color: 'default',
333
- autofocus: true,
334
- onClick: (e) => {
335
- dialog.close();
156
+ validations: [
157
+ {
158
+ rule: 'required',
336
159
  },
337
- },
338
- ],
339
- closeButton: false,
340
- });
341
- }
342
- resetReplyEditState() {
343
- this.isEditingMode.set(false);
344
- this.activeEditComment.set(undefined);
345
- this.isReplyingMode.set(false);
346
- this.activeReplyComment.set(undefined);
160
+ ],
161
+ },
162
+ {
163
+ name: 'title',
164
+ title: `t("title", { scope: "common" })`,
165
+ groupId: 'section',
166
+ options: {
167
+ sort: {
168
+ enabled: true,
169
+ },
170
+ filter: {
171
+ advance: {
172
+ enabled: true,
173
+ },
174
+ inline: {
175
+ enabled: false,
176
+ },
177
+ },
178
+ },
179
+ schema: {
180
+ dataType: 'string',
181
+ interface: {
182
+ type: AXPWidgetsCatalog.text,
183
+ },
184
+ },
185
+ validations: [
186
+ {
187
+ rule: 'required',
188
+ },
189
+ ],
190
+ },
191
+ ],
192
+ columns: [{ name: 'name' }, { name: 'title' }],
193
+ commands: {
194
+ create: {
195
+ execute: async (data) => {
196
+ const res = await dataService.insertOne(data);
197
+ return { id: res };
198
+ },
199
+ },
200
+ delete: {
201
+ execute: async (id) => {
202
+ return await dataService.deleteOne(id);
203
+ },
204
+ },
205
+ update: {
206
+ execute: async (data) => {
207
+ return await dataService.updateOne(data.id, data);
208
+ },
209
+ },
210
+ },
211
+ queries: {
212
+ byKey: {
213
+ execute: async (id) => {
214
+ return await dataService.getOne(id);
215
+ },
216
+ type: AXPEntityQueryType.Single,
217
+ },
218
+ list: {
219
+ execute: async (e) => {
220
+ return await dataService.query({ skip: e.skip, take: e.take, filter: e.filter });
221
+ },
222
+ type: AXPEntityQueryType.List,
223
+ },
224
+ },
225
+ interfaces: {
226
+ master: {
227
+ create: {
228
+ sections: [
229
+ {
230
+ id: 'section',
231
+ },
232
+ ],
233
+ properties: [
234
+ {
235
+ name: 'name',
236
+ layout: {
237
+ positions: {
238
+ lg: {
239
+ colSpan: 6,
240
+ order: 1,
241
+ },
242
+ },
243
+ },
244
+ },
245
+ {
246
+ name: 'title',
247
+ layout: {
248
+ positions: {
249
+ lg: {
250
+ colSpan: 6,
251
+ order: 2,
252
+ },
253
+ },
254
+ },
255
+ },
256
+ ],
257
+ },
258
+ update: {
259
+ sections: [
260
+ {
261
+ id: 'section',
262
+ },
263
+ ],
264
+ properties: [
265
+ {
266
+ name: 'name',
267
+ layout: {
268
+ positions: {
269
+ lg: {
270
+ colSpan: 6,
271
+ order: 1,
272
+ },
273
+ },
274
+ },
275
+ },
276
+ {
277
+ name: 'title',
278
+ layout: {
279
+ positions: {
280
+ lg: {
281
+ colSpan: 6,
282
+ order: 2,
283
+ },
284
+ },
285
+ },
286
+ },
287
+ ],
288
+ },
289
+ single: {
290
+ title: '{{title}}',
291
+ sections: [
292
+ {
293
+ id: 'section',
294
+ layout: {
295
+ positions: {
296
+ lg: {
297
+ colSpan: 12,
298
+ },
299
+ },
300
+ },
301
+ },
302
+ ],
303
+ properties: [
304
+ {
305
+ name: 'name',
306
+ layout: {
307
+ positions: {
308
+ lg: {
309
+ colSpan: 6,
310
+ order: 1,
311
+ },
312
+ },
313
+ },
314
+ },
315
+ {
316
+ name: 'title',
317
+ layout: {
318
+ positions: {
319
+ lg: {
320
+ colSpan: 6,
321
+ order: 2,
322
+ },
323
+ },
324
+ },
325
+ },
326
+ ],
327
+ actions: [],
328
+ },
329
+ list: {
330
+ actions: [
331
+ {
332
+ title: `t("create", { scope: "common" })`,
333
+ command: 'create-entity',
334
+ priority: 'primary',
335
+ type: 'create',
336
+ scope: AXPEntityCommandScope.TypeLevel,
337
+ },
338
+ {
339
+ title: 't("deleteItems", { scope: "common" })',
340
+ command: 'delete-entity',
341
+ priority: 'primary',
342
+ type: 'delete',
343
+ scope: AXPEntityCommandScope.Selected,
344
+ },
345
+ {
346
+ title: 't("detail", { scope: "common" })',
347
+ command: 'open-entity',
348
+ priority: 'secondary',
349
+ type: 'view',
350
+ scope: AXPEntityCommandScope.Individual,
351
+ },
352
+ {
353
+ title: 't("delete", { scope: "common" })',
354
+ command: 'delete-entity',
355
+ priority: 'secondary',
356
+ type: 'delete',
357
+ scope: AXPEntityCommandScope.Individual,
358
+ },
359
+ ],
360
+ views: [
361
+ createAllQueryView({
362
+ sorts: [{ name: 'name', dir: 'asc' }],
363
+ }),
364
+ ],
365
+ },
366
+ },
367
+ },
368
+ };
369
+ return entityDef;
370
+ }
371
+
372
+ // === Shared Types ===
373
+
374
+ class AXMRoomService extends AXMEntityCrudServiceImpl {
375
+ }
376
+ class AXMRoomServiceImpl extends AXMRoomService {
377
+ constructor() {
378
+ super(`${RootConfig.module.name}.${RootConfig.entities.room.name}`);
347
379
  }
348
- async toggleLike(comment, reply) {
349
- if (reply) {
350
- this.updateLikeStatus(comment.id, reply.id);
351
- const payload = {
352
- id: reply.id,
353
- messageId: reply.id,
354
- type: 'like',
380
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMRoomServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
381
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMRoomServiceImpl }); }
382
+ }
383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMRoomServiceImpl, decorators: [{
384
+ type: Injectable
385
+ }], ctorParameters: () => [] });
386
+
387
+ async function roomFactory(injector) {
388
+ const dataService = injector.get(AXMRoomService);
389
+ const i18n = RootConfig.config.i18n;
390
+ const entityDef = {
391
+ module: RootConfig.module.name,
392
+ name: RootConfig.entities.room.name,
393
+ source: '',
394
+ title: RootConfig.entities.room.title,
395
+ formats: {
396
+ individual: RootConfig.entities.room.title,
397
+ plural: RootConfig.entities.room.title,
398
+ searchResult: {
399
+ title: '{{ title }}',
400
+ description: RootConfig.module.title,
401
+ },
402
+ },
403
+ relatedEntities: [],
404
+ groups: [
405
+ {
406
+ id: 'section',
407
+ title: RootConfig.entities.room.title,
408
+ },
409
+ ],
410
+ properties: [
411
+ {
412
+ name: 'name',
413
+ title: `t("name", { scope: "common" })`,
414
+ groupId: 'section',
415
+ options: {
416
+ sort: {
417
+ enabled: true,
418
+ },
419
+ filter: {
420
+ advance: {
421
+ enabled: true,
422
+ },
423
+ inline: {
424
+ enabled: false,
425
+ },
426
+ },
427
+ },
428
+ schema: {
429
+ dataType: 'string',
430
+ interface: {
431
+ type: AXPWidgetsCatalog.text,
432
+ },
433
+ },
434
+ validations: [
435
+ {
436
+ rule: 'required',
437
+ },
438
+ ],
439
+ },
440
+ {
441
+ name: 'title',
442
+ title: `t("title", { scope: "common" })`,
443
+ groupId: 'section',
444
+ options: {
445
+ sort: {
446
+ enabled: true,
447
+ },
448
+ filter: {
449
+ advance: {
450
+ enabled: true,
451
+ },
452
+ inline: {
453
+ enabled: false,
454
+ },
455
+ },
456
+ },
457
+ schema: {
458
+ dataType: 'string',
459
+ interface: {
460
+ type: AXPWidgetsCatalog.text,
461
+ },
462
+ },
463
+ validations: [
464
+ {
465
+ rule: 'required',
466
+ },
467
+ ],
468
+ },
469
+ ],
470
+ columns: [{ name: 'name' }, { name: 'title' }],
471
+ commands: {
472
+ create: {
473
+ execute: async (data) => {
474
+ const res = await dataService.insertOne(data);
475
+ return { id: res };
476
+ },
477
+ },
478
+ delete: {
479
+ execute: async (id) => {
480
+ return await dataService.deleteOne(id);
481
+ },
482
+ },
483
+ update: {
484
+ execute: async (data) => {
485
+ return await dataService.updateOne(data.id, data);
486
+ },
487
+ },
488
+ },
489
+ queries: {
490
+ byKey: {
491
+ execute: async (id) => {
492
+ return await dataService.getOne(id);
493
+ },
494
+ type: AXPEntityQueryType.Single,
495
+ },
496
+ list: {
497
+ execute: async (e) => {
498
+ return await dataService.query({ skip: e.skip, take: e.take, filter: e.filter });
499
+ },
500
+ type: AXPEntityQueryType.List,
501
+ },
502
+ },
503
+ interfaces: {
504
+ master: {
505
+ create: {
506
+ sections: [
507
+ {
508
+ id: 'section',
509
+ },
510
+ ],
511
+ properties: [
512
+ {
513
+ name: 'name',
514
+ layout: {
515
+ positions: {
516
+ lg: {
517
+ colSpan: 6,
518
+ order: 1,
519
+ },
520
+ },
521
+ },
522
+ },
523
+ {
524
+ name: 'title',
525
+ layout: {
526
+ positions: {
527
+ lg: {
528
+ colSpan: 6,
529
+ order: 2,
530
+ },
531
+ },
532
+ },
533
+ },
534
+ ],
535
+ },
536
+ update: {
537
+ sections: [
538
+ {
539
+ id: 'section',
540
+ },
541
+ ],
542
+ properties: [
543
+ {
544
+ name: 'name',
545
+ layout: {
546
+ positions: {
547
+ lg: {
548
+ colSpan: 6,
549
+ order: 1,
550
+ },
551
+ },
552
+ },
553
+ },
554
+ {
555
+ name: 'title',
556
+ layout: {
557
+ positions: {
558
+ lg: {
559
+ colSpan: 6,
560
+ order: 2,
561
+ },
562
+ },
563
+ },
564
+ },
565
+ ],
566
+ },
567
+ single: {
568
+ title: '{{title}}',
569
+ sections: [
570
+ {
571
+ id: 'section',
572
+ layout: {
573
+ positions: {
574
+ lg: {
575
+ colSpan: 12,
576
+ },
577
+ },
578
+ },
579
+ },
580
+ ],
581
+ properties: [
582
+ {
583
+ name: 'name',
584
+ layout: {
585
+ positions: {
586
+ lg: {
587
+ colSpan: 6,
588
+ order: 1,
589
+ },
590
+ },
591
+ },
592
+ },
593
+ {
594
+ name: 'title',
595
+ layout: {
596
+ positions: {
597
+ lg: {
598
+ colSpan: 6,
599
+ order: 2,
600
+ },
601
+ },
602
+ },
603
+ },
604
+ ],
605
+ actions: [],
606
+ },
607
+ list: {
608
+ actions: [
609
+ {
610
+ title: `t("create", { scope: "common" })`,
611
+ command: 'create-entity',
612
+ priority: 'primary',
613
+ type: 'create',
614
+ scope: AXPEntityCommandScope.TypeLevel,
615
+ },
616
+ {
617
+ title: 't("deleteItems", { scope: "common" })',
618
+ command: 'delete-entity',
619
+ priority: 'primary',
620
+ type: 'delete',
621
+ scope: AXPEntityCommandScope.Selected,
622
+ },
623
+ {
624
+ title: 't("detail", { scope: "common" })',
625
+ command: 'open-entity',
626
+ priority: 'secondary',
627
+ type: 'view',
628
+ scope: AXPEntityCommandScope.Individual,
629
+ },
630
+ {
631
+ title: 't("delete", { scope: "common" })',
632
+ command: 'delete-entity',
633
+ priority: 'secondary',
634
+ type: 'delete',
635
+ scope: AXPEntityCommandScope.Individual,
636
+ },
637
+ ],
638
+ views: [
639
+ createAllQueryView({
640
+ sorts: [{ name: 'name', dir: 'asc' }],
641
+ }),
642
+ ],
643
+ },
644
+ },
645
+ },
646
+ };
647
+ return entityDef;
648
+ }
649
+
650
+ class AXMConversationModuleEntityProvider {
651
+ constructor(injector) {
652
+ this.injector = injector;
653
+ }
654
+ preload() {
655
+ const module = RootConfig.module.name;
656
+ return Array.from(Object.values(RootConfig.entities)).map((entity) => ({
657
+ module: module,
658
+ entity: entity.name,
659
+ }));
660
+ }
661
+ async get(moduleName, entityName) {
662
+ switch (entityName) {
663
+ case 'room':
664
+ return roomFactory(this.injector);
665
+ case 'message':
666
+ return messageFactory(this.injector);
667
+ default:
668
+ return null;
669
+ }
670
+ }
671
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModuleEntityProvider, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
672
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModuleEntityProvider }); }
673
+ }
674
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModuleEntityProvider, decorators: [{
675
+ type: Injectable
676
+ }], ctorParameters: () => [{ type: i0.Injector }] });
677
+
678
+ /**
679
+ * Abstract Chat Service defining the contract for chat functionality
680
+ */
681
+ class AXMChatService {
682
+ }
683
+ class AXMChatServiceImpl {
684
+ constructor() {
685
+ this.roomService = inject(AXMRoomService);
686
+ this.messageService = inject(AXMMessageService);
687
+ this.sessionService = inject(AXPSessionService);
688
+ this.usersService = inject(AXMUsersEntityService);
689
+ }
690
+ // === Helper Methods ===
691
+ getCurrentUser() {
692
+ const user = this.sessionService.user;
693
+ return {
694
+ id: user?.id || 'guest-user',
695
+ type: 'user',
696
+ };
697
+ }
698
+ async getUserInfo(userId) {
699
+ try {
700
+ const user = await this.usersService.getOne(userId);
701
+ return {
702
+ id: user.id,
703
+ type: 'user',
355
704
  };
356
- try {
357
- await this.commentService.like(payload);
705
+ }
706
+ catch (error) {
707
+ console.error(`Failed to get user info for ID: ${userId}`, error);
708
+ return {
709
+ id: userId,
710
+ type: 'user',
711
+ };
712
+ }
713
+ }
714
+ formatMessage(message) {
715
+ const currentUserId = this.getCurrentUser().id;
716
+ return {
717
+ ...message,
718
+ isFromCurrentUser: message.author.id === currentUserId,
719
+ formattedDate: new Date(message.createdAt || new Date()).toLocaleString(),
720
+ hasSeen: (message.seen || []).length > 0,
721
+ };
722
+ }
723
+ async formatMember(userRef) {
724
+ try {
725
+ const user = await this.usersService.getOne(userRef.id);
726
+ return {
727
+ id: user.id,
728
+ firstName: user.firstName || '',
729
+ lastName: user.lastName || '',
730
+ fullName: `${user.firstName || ''} ${user.lastName || ''}`.trim(),
731
+ avatar: '', // Default empty string since avatar doesn't exist in the model
732
+ isOnline: Math.random() > 0.5, // Mockup for demo
733
+ };
734
+ }
735
+ catch (error) {
736
+ console.error(`Failed to format member for ID: ${userRef.id}`, error);
737
+ return {
738
+ id: userRef.id,
739
+ firstName: 'Unknown',
740
+ lastName: 'User',
741
+ fullName: 'Unknown User',
742
+ avatar: '',
743
+ isOnline: false,
744
+ };
745
+ }
746
+ }
747
+ // === Room Operations ===
748
+ async getRoomList(skip = 0, take = 100) {
749
+ return this.roomService.query({ skip, take });
750
+ }
751
+ async getRoomDetails(roomId) {
752
+ return this.roomService.getOne(roomId);
753
+ }
754
+ async createRoom(roomData) {
755
+ return this.roomService.insertOne(roomData);
756
+ }
757
+ async addRoomMember(roomId, userId) {
758
+ const room = await this.getRoomDetails(roomId);
759
+ const userRef = await this.getUserInfo(userId);
760
+ const updatedMembers = [...room.members, userRef];
761
+ return this.roomService.updateOne(roomId, { members: updatedMembers });
762
+ }
763
+ async removeRoomMember(roomId, memberId) {
764
+ const room = await this.getRoomDetails(roomId);
765
+ const updatedMembers = room.members.filter((member) => member.id !== memberId);
766
+ return this.roomService.updateOne(roomId, { members: updatedMembers });
767
+ }
768
+ async deleteRoom(roomId) {
769
+ return this.roomService.deleteOne(roomId);
770
+ }
771
+ // === Message Operations ===
772
+ async listMessages(roomId, skip = 0, take = 100) {
773
+ return this.messageService.query({
774
+ skip,
775
+ take,
776
+ filter: {
777
+ field: 'roomId',
778
+ value: roomId,
779
+ operator: { type: 'equal' },
780
+ },
781
+ });
782
+ }
783
+ async sendMessage(roomId, content, contentType = 'text', replyId) {
784
+ const author = this.getCurrentUser();
785
+ const messageContent = {
786
+ content,
787
+ contentType,
788
+ };
789
+ const message = {
790
+ roomId,
791
+ message: messageContent,
792
+ author,
793
+ replyId,
794
+ };
795
+ const messageId = await this.messageService.insertOne(message);
796
+ return this.messageService.getOne(messageId);
797
+ }
798
+ async editMessage(messageId, content, contentType = 'text') {
799
+ const messageContent = {
800
+ content,
801
+ contentType,
802
+ };
803
+ return this.messageService.updateOne(messageId, { message: messageContent });
804
+ }
805
+ async deleteMessage(messageId) {
806
+ try {
807
+ await this.messageService.deleteOne(messageId);
808
+ return true;
809
+ }
810
+ catch (error) {
811
+ console.error('Failed to delete message:', error);
812
+ return false;
813
+ }
814
+ }
815
+ async markAsSeen(messageId) {
816
+ const author = this.getCurrentUser();
817
+ const message = await this.messageService.getOne(messageId);
818
+ // Check if this user has already seen the message
819
+ const alreadySeen = (message.seen || []).some((seen) => seen.author.id === author.id);
820
+ if (alreadySeen) {
821
+ return message;
822
+ }
823
+ const seen = message.seen || [];
824
+ const updatedSeen = [...seen, { author, type: 'read' }];
825
+ return this.messageService.updateOne(messageId, { seen: updatedSeen });
826
+ }
827
+ async addReaction(messageId, type) {
828
+ const author = this.getCurrentUser();
829
+ const message = await this.messageService.getOne(messageId);
830
+ // Check if this user has already added this reaction
831
+ const existingReaction = (message.reactions || []).find((reaction) => reaction.author.id === author.id && reaction.type === type);
832
+ if (existingReaction) {
833
+ // If the reaction already exists, remove it (toggle behavior)
834
+ return this.removeReaction(messageId, type);
835
+ }
836
+ const reactions = message.reactions || [];
837
+ const updatedReactions = [...reactions, { author, type }];
838
+ return this.messageService.updateOne(messageId, { reactions: updatedReactions });
839
+ }
840
+ async removeReaction(messageId, type) {
841
+ const author = this.getCurrentUser();
842
+ const message = await this.messageService.getOne(messageId);
843
+ const reactions = message.reactions || [];
844
+ const updatedReactions = reactions.filter((reaction) => !(reaction.author.id === author.id && reaction.type === type));
845
+ return this.messageService.updateOne(messageId, { reactions: updatedReactions });
846
+ }
847
+ // === UI-specific Operations ===
848
+ async markChatAsRead(roomId) {
849
+ try {
850
+ // Get all unread messages for this room
851
+ const messages = await this.listMessages(roomId);
852
+ const currentUserId = this.getCurrentUser().id;
853
+ // Mark each unread message as seen
854
+ const markPromises = messages.items
855
+ .filter((message) => {
856
+ // Only mark messages that are not from the current user and not already seen by them
857
+ const isFromCurrentUser = message.author.id === currentUserId;
858
+ const isAlreadySeen = (message.seen || []).some((seen) => seen.author.id === currentUserId);
859
+ return !isFromCurrentUser && !isAlreadySeen;
860
+ })
861
+ .map((message) => this.markAsSeen(message.id));
862
+ await Promise.all(markPromises);
863
+ return true;
864
+ }
865
+ catch (error) {
866
+ console.error('Failed to mark chat as read:', error);
867
+ return false;
868
+ }
869
+ }
870
+ async getUnreadCount(roomId) {
871
+ const messages = await this.listMessages(roomId);
872
+ const currentUserId = this.getCurrentUser().id;
873
+ return messages.items.filter((message) => {
874
+ // Count messages that are not from the current user and not already seen by them
875
+ const isFromCurrentUser = message.author.id === currentUserId;
876
+ const isAlreadySeen = (message.seen || []).some((seen) => seen.author.id === currentUserId);
877
+ return !isFromCurrentUser && !isAlreadySeen;
878
+ }).length;
879
+ }
880
+ async getChatRoom(roomId) {
881
+ const room = await this.getRoomDetails(roomId);
882
+ const messages = await this.listMessages(roomId, 0, 1);
883
+ const lastMessage = messages.items.length > 0 ? this.formatMessage(messages.items[0]) : undefined;
884
+ const unreadCount = await this.getUnreadCount(roomId);
885
+ const roomMembers = await Promise.all(room.members.map((member) => this.formatMember(member)));
886
+ return {
887
+ ...room,
888
+ lastMessage,
889
+ unreadCount,
890
+ roomMembers,
891
+ };
892
+ }
893
+ async getAllChatRooms(skip = 0, take = 100) {
894
+ const rooms = await this.getRoomList(skip, take);
895
+ const chatRooms = await Promise.all(rooms.items.map(async (room) => {
896
+ const messages = await this.listMessages(room.id, 0, 1);
897
+ const lastMessage = messages.items.length > 0 ? this.formatMessage(messages.items[0]) : undefined;
898
+ const unreadCount = await this.getUnreadCount(room.id);
899
+ const roomMembers = await Promise.all(room.members.map((member) => this.formatMember(member)));
900
+ return {
901
+ ...room,
902
+ lastMessage,
903
+ unreadCount,
904
+ roomMembers,
905
+ };
906
+ }));
907
+ return {
908
+ items: chatRooms,
909
+ total: rooms.total,
910
+ };
911
+ }
912
+ async getChatMessages(roomId, skip = 0, take = 20) {
913
+ const result = await this.listMessages(roomId, skip, take);
914
+ const formattedMessages = result.items.map((message) => this.formatMessage(message));
915
+ return {
916
+ items: formattedMessages,
917
+ total: result.total,
918
+ };
919
+ }
920
+ async createNewConversation(memberIds) {
921
+ // Add current user if not already included
922
+ const currentUser = this.getCurrentUser();
923
+ const hasCurrentUser = memberIds.includes(currentUser.id);
924
+ const allMemberIds = hasCurrentUser ? memberIds : [...memberIds, currentUser.id];
925
+ try {
926
+ // Get user info for all members
927
+ const memberReferences = await Promise.all(allMemberIds.map((id) => this.getUserInfo(id)));
928
+ // Get user names for title
929
+ const userNames = await Promise.all(memberReferences.map(async (ref) => {
930
+ try {
931
+ const user = await this.usersService.getOne(ref.id);
932
+ return `${user.firstName} ${user.lastName}`.trim();
933
+ }
934
+ catch (error) {
935
+ return ref.id;
936
+ }
937
+ }));
938
+ // Create a new room - this returns just the ID as a string
939
+ const roomId = await this.createRoom({
940
+ members: memberReferences,
941
+ title: userNames.join(', '),
942
+ });
943
+ // Get the full room data with the ID
944
+ return this.getChatRoom(roomId);
945
+ }
946
+ catch (error) {
947
+ console.error('Failed to create conversation:', error);
948
+ throw error;
949
+ }
950
+ }
951
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
952
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatServiceImpl, providedIn: 'root' }); }
953
+ }
954
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatServiceImpl, decorators: [{
955
+ type: Injectable,
956
+ args: [{
957
+ providedIn: 'root',
958
+ }]
959
+ }] });
960
+
961
+ class AXMChatItemComponent {
962
+ constructor() {
963
+ // Input
964
+ this.data = input.required();
965
+ // Output
966
+ this.pressChatItem = output();
967
+ // Services
968
+ this.sessionService = inject(AXPSessionService);
969
+ this.router = inject(Router);
970
+ this.activatedRoute = inject(ActivatedRoute);
971
+ // Computed values
972
+ this.fullName = computed(() => {
973
+ const member = this.data().roomMembers?.[0];
974
+ return member ? member.fullName : 'Unknown User';
975
+ });
976
+ this.lastMessage = computed(() => {
977
+ return this.data().lastMessage?.message?.content || 'No messages yet';
978
+ });
979
+ this.lastMessageDate = computed(() => {
980
+ const message = this.data().lastMessage;
981
+ if (!message)
982
+ return '';
983
+ return message.formattedDate || '';
984
+ });
985
+ this.hasUnread = computed(() => {
986
+ return this.data().unreadCount > 0;
987
+ });
988
+ this.unreadCount = computed(() => {
989
+ return this.data().unreadCount.toString();
990
+ });
991
+ this.isOnline = computed(() => {
992
+ const member = this.data().roomMembers?.[0];
993
+ return member?.isOnline || false;
994
+ });
995
+ this.myId = this.sessionService.user?.id;
996
+ }
997
+ // Methods
998
+ extractInitials(name) {
999
+ if (!name)
1000
+ return '?';
1001
+ const words = name.split(' ');
1002
+ const initials = words.map((word) => word.charAt(0).toUpperCase());
1003
+ return initials.join('');
1004
+ }
1005
+ onPressChatItem(id) {
1006
+ this.pressChatItem.emit(id);
1007
+ }
1008
+ messageSeenStatus() {
1009
+ const lastMessage = this.data().lastMessage;
1010
+ if (!lastMessage)
1011
+ return '';
1012
+ // Check if current user is the author
1013
+ if (this.myId === lastMessage.author?.id) {
1014
+ // Check if message has been seen by anyone
1015
+ return lastMessage.hasSeen ? 'ax-icon-dobble-check' : 'ax-icon-check';
1016
+ }
1017
+ return '';
1018
+ }
1019
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1020
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMChatItemComponent, isStandalone: false, selector: "axm-chat-item", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, 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-transition-all ax-duration-200 hover:ax-bg-surface-hover ax-rounded-lg 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 <ax-avatar class=\"ax-relative\" color=\"primary\" shape=\"rounded\" [size]=\"48\">\n @if (data().roomMembers && data().roomMembers.length > 0 && data().roomMembers[0]?.avatar) {\n <ax-image [src]=\"data().roomMembers[0].avatar\" class=\"ax-rounded-lg\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-font-medium\">{{ extractInitials(fullName()) }}</span>\n </ax-text>\n }\n\n <!-- Online Status Indicator -->\n @if (isOnline()) {\n <ax-badge class=\"ax-absolute ax-bottom-1 ax-end-1 !ax-size-3 !ax-border-2 !ax-border-surface\" color=\"success\">\n </ax-badge>\n }\n </ax-avatar>\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-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\">{{ fullName() }}</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 (lastMessageDate()) {\n <span class=\"ax-text-xs ax-text-on-surface-variant ax-whitespace-nowrap\">\n {{ lastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-mt-1\">\n <p class=\"ax-text-sm ax-text-on-surface-variant ax-truncate\">\n {{ lastMessage() }}\n </p>\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: i1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "component", type: i2.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "component", type: i3.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { 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: "component", type: i5$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1021
+ }
1022
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemComponent, decorators: [{
1023
+ type: Component,
1024
+ args: [{ selector: 'axm-chat-item', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, 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-transition-all ax-duration-200 hover:ax-bg-surface-hover ax-rounded-lg 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 <ax-avatar class=\"ax-relative\" color=\"primary\" shape=\"rounded\" [size]=\"48\">\n @if (data().roomMembers && data().roomMembers.length > 0 && data().roomMembers[0]?.avatar) {\n <ax-image [src]=\"data().roomMembers[0].avatar\" class=\"ax-rounded-lg\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-font-medium\">{{ extractInitials(fullName()) }}</span>\n </ax-text>\n }\n\n <!-- Online Status Indicator -->\n @if (isOnline()) {\n <ax-badge class=\"ax-absolute ax-bottom-1 ax-end-1 !ax-size-3 !ax-border-2 !ax-border-surface\" color=\"success\">\n </ax-badge>\n }\n </ax-avatar>\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-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\">{{ fullName() }}</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 (lastMessageDate()) {\n <span class=\"ax-text-xs ax-text-on-surface-variant ax-whitespace-nowrap\">\n {{ lastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-mt-1\">\n <p class=\"ax-text-sm ax-text-on-surface-variant ax-truncate\">\n {{ lastMessage() }}\n </p>\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n </div>\n </div>\n </div>\n</div>\n" }]
1025
+ }] });
1026
+
1027
+ class AXMChatComponent {
1028
+ constructor() {
1029
+ this.route = inject(ActivatedRoute);
1030
+ this.router = inject(Router);
1031
+ this.chatService = inject(AXMChatService);
1032
+ // View Children
1033
+ this.tab = viewChild('tab');
1034
+ // State signals
1035
+ this.rooms = signal([]);
1036
+ this.allRooms = signal([]);
1037
+ this.selectedRoom = signal(null);
1038
+ this.isLoading = signal(false);
1039
+ this.error = signal(null);
1040
+ this.activeTab = signal(0);
1041
+ this.isSearching = signal(false);
1042
+ this.searchQuery = signal('');
1043
+ this.showSearch = signal(true);
1044
+ this.placeholder = signal('Search chats...');
1045
+ // Computed signals
1046
+ this.filteredRooms = computed(() => {
1047
+ let chatRooms = this.rooms();
1048
+ // Apply search filter
1049
+ if (this.searchQuery()) {
1050
+ const searchText = this.searchQuery().toLowerCase();
1051
+ chatRooms = chatRooms.filter((room) => {
1052
+ const roomName = room.roomMembers?.[0]?.fullName || '';
1053
+ const lastMessage = room.lastMessage?.message?.content || '';
1054
+ return roomName.toLowerCase().includes(searchText) || lastMessage.toLowerCase().includes(searchText);
1055
+ });
1056
+ }
1057
+ // Apply tab filter
1058
+ if (this.activeTab() === 1) {
1059
+ return chatRooms.filter((room) => room.unreadCount > 0);
1060
+ }
1061
+ return chatRooms;
1062
+ });
1063
+ this.unreadCount = computed(() => this.allRooms().filter((i) => i.unreadCount > 0).length);
1064
+ this.allCount = computed(() => this.allRooms().length);
1065
+ this.hasUnread = computed(() => this.allRooms().filter((i) => i.unreadCount > 0).length > 0);
1066
+ this.totalCount = computed(() => this.allRooms().length || 0);
1067
+ afterNextRender(() => {
1068
+ const tabComponent = this.tab();
1069
+ if (tabComponent) {
1070
+ tabComponent.onActiveTabChanged.subscribe((i) => {
1071
+ this.activeTab.set(i.index);
1072
+ });
1073
+ }
1074
+ });
1075
+ }
1076
+ async ngOnInit() {
1077
+ await this.loadChats();
1078
+ }
1079
+ // Page Interface Implementation
1080
+ getPageTitle() {
1081
+ return this.selectedRoom()
1082
+ ? this.selectedRoom()?.roomMembers?.[0]?.fullName || 'Unknown User'
1083
+ : 'Select A Chat First';
1084
+ }
1085
+ getPageDescription() {
1086
+ return this.selectedRoom() ? this.selectedRoom()?.title || '' : '';
1087
+ }
1088
+ getMenuItems() {
1089
+ return [];
1090
+ }
1091
+ // Methods
1092
+ async loadChats() {
1093
+ try {
1094
+ this.isLoading.set(true);
1095
+ this.error.set(null);
1096
+ // Use the updated service method to get rooms with last messages
1097
+ const response = await this.chatService.getAllChatRooms();
1098
+ this.allRooms.set(response.items);
1099
+ this.rooms.set(response.items);
1100
+ this.isLoading.set(false);
1101
+ }
1102
+ catch (error) {
1103
+ this.error.set('Failed to load chats. Please try again.');
1104
+ this.isLoading.set(false);
1105
+ console.error('Error loading chats:', error);
1106
+ }
1107
+ }
1108
+ async refreshChat() {
1109
+ await this.loadChats();
1110
+ }
1111
+ async markChatAsRead(chatId) {
1112
+ try {
1113
+ // Use the service method to mark chat as read
1114
+ await this.chatService.markChatAsRead(chatId);
1115
+ // Update local state without making another network request
1116
+ const currentRooms = this.allRooms();
1117
+ const updatedRooms = currentRooms.map((room) => {
1118
+ if (room.id === chatId) {
1119
+ // Reset unread count for the selected room
1120
+ return { ...room, unreadCount: 0 };
1121
+ }
1122
+ return room;
1123
+ });
1124
+ // Update the rooms signals
1125
+ this.allRooms.set(updatedRooms);
1126
+ this.rooms.set(updatedRooms);
1127
+ // Find the selected room
1128
+ const selectedRoom = updatedRooms.find((room) => room.id === chatId) || null;
1129
+ this.selectedRoom.set(selectedRoom);
1130
+ // Navigate to the chat details
1131
+ this.router.navigate([chatId], { relativeTo: this.route });
1132
+ }
1133
+ catch (error) {
1134
+ console.error('Failed to mark chat as read:', error);
1135
+ }
1136
+ }
1137
+ searchChat(query) {
1138
+ this.searchQuery.set(query);
1139
+ this.isSearching.set(!!query);
1140
+ }
1141
+ goBack() {
1142
+ this.selectedRoom.set(null);
1143
+ this.router.navigate(['./'], { relativeTo: this.route });
1144
+ }
1145
+ // Header methods
1146
+ onSearch(query) {
1147
+ this.searchChat(query);
1148
+ }
1149
+ clearSearch() {
1150
+ this.searchQuery.set('');
1151
+ this.isSearching.set(false);
1152
+ }
1153
+ // Footer methods
1154
+ onNewConversation() {
1155
+ // TODO: Implement new conversation logic
1156
+ console.log('New conversation clicked');
1157
+ }
1158
+ // Preview header methods
1159
+ emitBack() {
1160
+ this.goBack();
1161
+ }
1162
+ ngOnDestroy() {
1163
+ console.log('ngOnDestroy');
1164
+ }
1165
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1166
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMChatComponent, isStandalone: false, selector: "axm-chat", viewQueries: [{ propertyName: "tab", first: true, predicate: ["tab"], descendants: true, isSignal: true }], ngImport: i0, template: "<axp-page-layout>\n <axp-layout-start-side [axResizable]=\"true\" class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>Conversations</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [ngModel]=\"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\">\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n <ax-tabs #tab class=\"ax-text-neutral-400\" [look]=\"'with-line'\" [location]=\"'bottom'\" [fitParent]=\"true\">\n <ax-tab-item text=\"All\" [active]=\"activeTab() === 0\">\n <ax-suffix>\n <ax-badge\n [text]=\"totalCount().toString()\"\n [color]=\"activeTab() === 0 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n @if (hasUnread()) {\n <ax-tab-item text=\"Unread\" [active]=\"activeTab() === 1\">\n <ax-suffix>\n <ax-badge\n [text]=\"unreadCount().toString()\"\n [color]=\"activeTab() === 1 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n\n <!-- Chat List Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col\">\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-p-4\">\n <ax-loading></ax-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-p-4 ax-text-error ax-text-center\">\n {{ error() }}\n <button (click)=\"refreshChat()\" class=\"ax-mt-2 ax-text-primary hover:ax-text-primary-dark\">\n Try Again\n </button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div\n class=\"ax-flex-1 ax-overflow-y-auto ax-space-y-2 ax-scrollbar-thin ax-scrollbar-thumb-gray-300 ax-scrollbar-track-transparent\"\n >\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n (click)=\"markChatAsRead(i.id)\"\n [class.ax-bg-surface]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-200 hover:ax-bg-surface ax-rounded-lg\"\n ></axm-chat-item>\n }\n\n <!-- Empty State -->\n @empty {\n <div class=\"ax-p-4 ax-font-medium ax-text-center ax-text-secondary\">\n @if (isSearching()) {\n No chats found matching your search\n } @else {\n No chats available\n }\n </div>\n }\n </div>\n }\n\n <!-- Footer Content -->\n <div class=\"ax-border-t ax-border-divider ax-bg-lightest ax-p-4\">\n <ax-button class=\"ax-w-full\" color=\"primary\" text=\"New Conversation\" (onClick)=\"onNewConversation()\">\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 </div>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content>\n <!-- Chat Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden\">\n <router-outlet></router-outlet>\n </div>\n </axp-page-content>\n</axp-page-layout>\n", styles: [""], dependencies: [{ 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: i2$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: i3$1.AXResizableDirective, selector: "[axResizable]", inputs: ["axResizable", "minWidth", "maxWidth", "dblClickAction", "width", "defaultWidth"], outputs: ["axResizableChange", "minWidthChange", "maxWidthChange", "dblClickActionChange", "widthChange", "defaultWidthChange", "onResizingStarted", "onResizingEnded", "onResizingDblClick"] }, { kind: "component", type: i3.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { 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: "component", type: i6.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { 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: "component", type: i6$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.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: i10.AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: i11.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: i11.AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: i11.AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: i11.AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }, { kind: "component", type: AXMChatItemComponent, selector: "axm-chat-item", inputs: ["data"], outputs: ["pressChatItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1167
+ }
1168
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatComponent, decorators: [{
1169
+ type: Component,
1170
+ args: [{ selector: 'axm-chat', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<axp-page-layout>\n <axp-layout-start-side [axResizable]=\"true\" class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>Conversations</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [ngModel]=\"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\">\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n <ax-tabs #tab class=\"ax-text-neutral-400\" [look]=\"'with-line'\" [location]=\"'bottom'\" [fitParent]=\"true\">\n <ax-tab-item text=\"All\" [active]=\"activeTab() === 0\">\n <ax-suffix>\n <ax-badge\n [text]=\"totalCount().toString()\"\n [color]=\"activeTab() === 0 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n @if (hasUnread()) {\n <ax-tab-item text=\"Unread\" [active]=\"activeTab() === 1\">\n <ax-suffix>\n <ax-badge\n [text]=\"unreadCount().toString()\"\n [color]=\"activeTab() === 1 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n\n <!-- Chat List Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col\">\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-p-4\">\n <ax-loading></ax-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-p-4 ax-text-error ax-text-center\">\n {{ error() }}\n <button (click)=\"refreshChat()\" class=\"ax-mt-2 ax-text-primary hover:ax-text-primary-dark\">\n Try Again\n </button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div\n class=\"ax-flex-1 ax-overflow-y-auto ax-space-y-2 ax-scrollbar-thin ax-scrollbar-thumb-gray-300 ax-scrollbar-track-transparent\"\n >\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n (click)=\"markChatAsRead(i.id)\"\n [class.ax-bg-surface]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-200 hover:ax-bg-surface ax-rounded-lg\"\n ></axm-chat-item>\n }\n\n <!-- Empty State -->\n @empty {\n <div class=\"ax-p-4 ax-font-medium ax-text-center ax-text-secondary\">\n @if (isSearching()) {\n No chats found matching your search\n } @else {\n No chats available\n }\n </div>\n }\n </div>\n }\n\n <!-- Footer Content -->\n <div class=\"ax-border-t ax-border-divider ax-bg-lightest ax-p-4\">\n <ax-button class=\"ax-w-full\" color=\"primary\" text=\"New Conversation\" (onClick)=\"onNewConversation()\">\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 </div>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content>\n <!-- Chat Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden\">\n <router-outlet></router-outlet>\n </div>\n </axp-page-content>\n</axp-page-layout>\n" }]
1171
+ }], ctorParameters: () => [] });
1172
+
1173
+ /**
1174
+ * Converts chat service response to chat preview format
1175
+ * @param chatResponse - The response from chat service containing items and total
1176
+ * @returns Array of formatted chat preview messages
1177
+ */
1178
+ function convertToChatPreview(chatResponse) {
1179
+ if (!chatResponse || !chatResponse.items?.length) {
1180
+ return [];
1181
+ }
1182
+ // Map from AXMChatMessage to ChatPreviewMessage
1183
+ return chatResponse.items.map((message) => {
1184
+ // Determine message type based on content type
1185
+ let messageType = 'text';
1186
+ // Map contentType to preview type
1187
+ if (message.message?.contentType) {
1188
+ switch (message.message.contentType) {
1189
+ case 'text':
1190
+ messageType = 'text';
1191
+ break;
1192
+ case 'file':
1193
+ messageType = 'file';
1194
+ break;
1195
+ case 'image':
1196
+ messageType = 'image';
1197
+ break;
1198
+ case 'video':
1199
+ messageType = 'video';
1200
+ break;
1201
+ case 'link':
1202
+ // Determine if link is for audio or other content
1203
+ const content = message.message.content || '';
1204
+ if (content.includes('.mp3') || content.includes('.ogg') || content.includes('audio')) {
1205
+ messageType = 'audio';
1206
+ }
1207
+ else {
1208
+ messageType = 'text';
1209
+ }
1210
+ break;
1211
+ default:
1212
+ messageType = 'text';
1213
+ }
1214
+ }
1215
+ // Extract author name if available
1216
+ const name = message.author ? message.author.id : 'Unknown';
1217
+ // Create the chat preview message
1218
+ const previewMessage = {
1219
+ id: message.id,
1220
+ sendTime: new Date(message.createdAt || new Date()),
1221
+ readTime: new Date(), // Assuming "read" when converted
1222
+ type: messageType,
1223
+ content: message.message?.content || '',
1224
+ name: name,
1225
+ };
1226
+ // Add fromId if the message is not from current user
1227
+ if (!message.isFromCurrentUser) {
1228
+ previewMessage.fromId = message.author?.id;
1229
+ }
1230
+ // Note: Reply information would need to be looked up from the actual messages
1231
+ // This would typically be done by the service that has access to all messages
1232
+ return previewMessage;
1233
+ });
1234
+ }
1235
+ /**
1236
+ * Creates a ChatPreviewMessage from reply information
1237
+ * @param message - The original message that is being referenced
1238
+ * @returns A ChatPreviewMessage formatted for use as a replyTo
1239
+ */
1240
+ function createReplyPreview(message) {
1241
+ if (!message)
1242
+ return undefined;
1243
+ return {
1244
+ id: message.id,
1245
+ sendTime: new Date(message.createdAt || new Date()),
1246
+ readTime: new Date(),
1247
+ type: message.message?.contentType === 'image'
1248
+ ? 'image'
1249
+ : message.message?.contentType === 'video'
1250
+ ? 'video'
1251
+ : message.message?.contentType === 'file'
1252
+ ? 'file'
1253
+ : 'text',
1254
+ content: message.message?.content || '',
1255
+ name: message.author?.id || 'Unknown',
1256
+ fromId: !message.isFromCurrentUser ? message.author?.id : undefined,
1257
+ };
1258
+ }
1259
+ /**
1260
+ * Generate demo chat preview data with a variety of message types
1261
+ * @returns Array of sample chat preview messages
1262
+ */
1263
+ function generateDemoChatPreview() {
1264
+ return [
1265
+ {
1266
+ id: '0',
1267
+ sendTime: new Date(),
1268
+ type: 'text',
1269
+ readTime: new Date(),
1270
+ content: 'Hello John, How are you?',
1271
+ name: 'test name',
1272
+ },
1273
+ {
1274
+ id: '1',
1275
+ fromId: '10',
1276
+ sendTime: new Date(),
1277
+ readTime: new Date(),
1278
+ type: 'text',
1279
+ content: 'Hello',
1280
+ name: 'test name',
1281
+ replyTo: {
1282
+ id: '0',
1283
+ sendTime: new Date(),
1284
+ type: 'text',
1285
+ readTime: new Date(),
1286
+ content: 'Hello John, How are you?',
1287
+ name: 'test name',
1288
+ },
1289
+ },
1290
+ {
1291
+ id: '2',
1292
+ fromId: '10',
1293
+ sendTime: new Date(),
1294
+ readTime: new Date(),
1295
+ type: 'voice',
1296
+ name: 'test name',
1297
+ content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
1298
+ },
1299
+ {
1300
+ id: '3',
1301
+ sendTime: new Date(),
1302
+ readTime: new Date(),
1303
+ type: 'text',
1304
+ content: 'Can i have your address information?',
1305
+ name: 'test name',
1306
+ replyTo: {
1307
+ id: '1',
1308
+ fromId: '10',
1309
+ sendTime: new Date(),
1310
+ readTime: new Date(),
1311
+ type: 'text',
1312
+ content: 'Hello',
1313
+ name: 'test name',
1314
+ },
1315
+ },
1316
+ {
1317
+ id: '4',
1318
+ fromId: '10',
1319
+ sendTime: new Date(),
1320
+ readTime: new Date(),
1321
+ type: 'file',
1322
+ name: 'test name',
1323
+ content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
1324
+ },
1325
+ {
1326
+ id: '5',
1327
+ sendTime: new Date(),
1328
+ readTime: new Date(),
1329
+ type: 'image',
1330
+ name: 'test name',
1331
+ content: `https://picsum.photos/300/200`,
1332
+ },
1333
+ {
1334
+ id: '6',
1335
+ sendTime: new Date(),
1336
+ readTime: new Date(),
1337
+ type: 'video',
1338
+ name: 'test name',
1339
+ content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
1340
+ },
1341
+ {
1342
+ id: '7',
1343
+ sendTime: new Date(),
1344
+ type: 'text',
1345
+ readTime: new Date(),
1346
+ content: 'Hello John, How are you?',
1347
+ name: 'test name',
1348
+ },
1349
+ {
1350
+ id: '8',
1351
+ fromId: '10',
1352
+ sendTime: new Date(),
1353
+ readTime: new Date(),
1354
+ type: 'audio',
1355
+ name: 'kids',
1356
+ content: `https://actions.google.com/sounds/v1/ambiences/kids_playing.ogg`,
1357
+ },
1358
+ {
1359
+ id: '9',
1360
+ fromId: '10',
1361
+ sendTime: new Date(),
1362
+ readTime: new Date(),
1363
+ type: 'image',
1364
+ name: 'test name',
1365
+ content: `https://picsum.photos/300/200`,
1366
+ },
1367
+ {
1368
+ id: '10',
1369
+ fromId: '10',
1370
+ sendTime: new Date(),
1371
+ readTime: new Date(),
1372
+ type: 'image',
1373
+ name: 'test name',
1374
+ content: `https://picsum.photos/200/300`,
1375
+ },
1376
+ {
1377
+ id: '11',
1378
+ fromId: '10',
1379
+ sendTime: new Date(),
1380
+ readTime: new Date(),
1381
+ type: 'video',
1382
+ name: 'test name',
1383
+ content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
1384
+ },
1385
+ {
1386
+ id: '12',
1387
+ sendTime: new Date(),
1388
+ readTime: new Date(),
1389
+ type: 'file',
1390
+ name: 'test name',
1391
+ content: `data:audio/webm;codecs=opus;base64,GkXfo59ChoE+u5BxHVL7ZAS1EF34fJ`,
1392
+ },
1393
+ {
1394
+ id: '13',
1395
+ sendTime: new Date(),
1396
+ readTime: new Date(),
1397
+ type: 'audio',
1398
+ name: 'alarm',
1399
+ content: `https://actions.google.com/sounds/v1/alarms/digital_watch_alarm_long.ogg`,
1400
+ },
1401
+ ];
1402
+ }
1403
+ /**
1404
+ * Usage Examples:
1405
+ *
1406
+ * Example 1: Converting chat service response to chat preview format
1407
+ * ```typescript
1408
+ * import { AXMChatService } from '../chat.service';
1409
+ * import { convertToChatPreview } from './chat-preview.helper';
1410
+ *
1411
+ * export class ChatPreviewComponent {
1412
+ * constructor(private chatService: AXMChatService) {}
1413
+ *
1414
+ * async loadChatMessages(roomId: string) {
1415
+ * const response = await this.chatService.getChatMessages(roomId);
1416
+ *
1417
+ * // Convert to chat preview format
1418
+ * const previewMessages = convertToChatPreview(response);
1419
+ *
1420
+ * // Use the converted messages
1421
+ * this.conversationService.chats.set(previewMessages);
1422
+ * }
1423
+ * }
1424
+ * ```
1425
+ *
1426
+ * Example 2: Using demo data for development/testing
1427
+ * ```typescript
1428
+ * import { generateDemoChatPreview } from './chat-preview.helper';
1429
+ *
1430
+ * export class ChatPreviewDemoComponent {
1431
+ * ngOnInit() {
1432
+ * // Load demo messages
1433
+ * const demoMessages = generateDemoChatPreview();
1434
+ *
1435
+ * // Use the demo messages
1436
+ * this.conversationService.chats.set(demoMessages);
1437
+ * }
1438
+ * }
1439
+ */
1440
+
1441
+ class AXMChatPreviewComponent {
1442
+ constructor(elRef) {
1443
+ this.elRef = elRef;
1444
+ // Signal for dynamic max-height (using computed logic for real-time updates)
1445
+ this.activatedRoute = inject(ActivatedRoute);
1446
+ this.fileService = inject(AXFileService);
1447
+ this.conversationService = inject(AXConversationService);
1448
+ this.chatService = inject(AXMChatService);
1449
+ this.options = signal({
1450
+ disabled: false,
1451
+ readonly: false,
1452
+ value: '',
1453
+ });
1454
+ this.conversationViewMaxHeight = signal('');
1455
+ this.initialTextAreaHeight = 0;
1456
+ this.textareaHeight = signal(0);
1457
+ this.dynamicHeight = computed(() => {
1458
+ const baseOffset = 275;
1459
+ const currentHeight = this.textareaHeight();
1460
+ const dynamicOffset = baseOffset + (currentHeight - this.initialTextAreaHeight);
1461
+ return `calc(100vh - ${dynamicOffset}px) !important`;
1462
+ });
1463
+ afterNextRender(() => {
1464
+ const textareaContainer = this.elRef.nativeElement.querySelector('ax-conversation-input > div');
1465
+ if (textareaContainer) {
1466
+ this.initialTextAreaHeight = textareaContainer.offsetHeight;
1467
+ this.resizeObserver = new ResizeObserver((entries) => {
1468
+ for (const entry of entries) {
1469
+ if (entry.target === textareaContainer) {
1470
+ const currentHeight = entry.contentRect.height;
1471
+ this.textareaHeight.set(currentHeight);
1472
+ }
1473
+ }
1474
+ });
1475
+ this.resizeObserver.observe(textareaContainer);
358
1476
  }
359
- catch (error) {
360
- this.updateLikeStatus(comment.id, reply.id);
1477
+ else {
1478
+ console.warn('Textarea Container element not found.');
361
1479
  }
1480
+ this.activatedRoute.params.subscribe((params) => {
1481
+ console.log(params['id']);
1482
+ this.messageId = params['id'];
1483
+ this.loadMessages(this.messageId);
1484
+ });
1485
+ });
1486
+ }
1487
+ ngOnDestroy() {
1488
+ if (this.resizeObserver) {
1489
+ this.resizeObserver.disconnect();
362
1490
  }
363
- else {
364
- this.updateLikeStatus(comment.id);
365
- const payload = {
366
- id: comment.id,
367
- messageId: comment.id,
368
- type: 'like',
1491
+ }
1492
+ handleFileChange(event) {
1493
+ console.log('File Changed:', event);
1494
+ }
1495
+ handleCancelRecord(event) {
1496
+ console.log('Recording Cancelled:', event);
1497
+ }
1498
+ handleEndRecord(event) {
1499
+ this.fileService.blobToBase64(event.data.value).then((base64Content) => {
1500
+ // Create a properly formatted chat preview message for voice
1501
+ const newVoiceMessage = {
1502
+ id: `${Math.floor(Math.random() * 10000)}`,
1503
+ content: base64Content,
1504
+ sendTime: new Date(),
1505
+ readTime: new Date(),
1506
+ type: 'voice',
1507
+ name: 'test name',
369
1508
  };
370
- try {
371
- await this.commentService.like(payload);
372
- }
373
- catch (error) {
374
- this.updateLikeStatus(comment.id);
1509
+ this.conversationService.chats.update((values) => [...values, newVoiceMessage]);
1510
+ });
1511
+ }
1512
+ handleOnSend(e) {
1513
+ console.log('Message Sent:', e);
1514
+ if (e.data.value) {
1515
+ this.options.update((prev) => ({ ...prev, value: '' }));
1516
+ // Create a properly formatted chat preview message
1517
+ const newMessage = {
1518
+ id: `${Math.floor(Math.random() * 10000)}`,
1519
+ content: e.data.value,
1520
+ sendTime: new Date(),
1521
+ readTime: new Date(),
1522
+ type: e.data.type || 'text',
1523
+ name: 'test name',
1524
+ replyTo: e.data.replyChat,
1525
+ };
1526
+ // If it's a reply to another message, ensure it has the right format
1527
+ if (newMessage.replyTo) {
1528
+ // Ensure replyTo has the right format if it doesn't already
1529
+ if (!newMessage.replyTo.readTime) {
1530
+ newMessage.replyTo = {
1531
+ ...newMessage.replyTo,
1532
+ readTime: new Date(),
1533
+ };
1534
+ }
375
1535
  }
1536
+ this.conversationService.chats.update((values) => [...values, newMessage]);
376
1537
  }
1538
+ this.scrollToEnd();
377
1539
  }
378
- updateLikeStatus(commentId, replyId) {
379
- this.comments.update((commentsList) => {
380
- return commentsList.map((comment) => {
381
- if (comment.id === commentId) {
382
- if (replyId) {
383
- const updatedReplies = comment.replies?.map((reply) => reply.id === replyId
384
- ? {
385
- ...reply,
386
- isLiked: !reply.isLiked,
387
- reactionsCount: reply.isLiked ? --reply.reactionsCount : ++reply.reactionsCount,
388
- }
389
- : reply);
390
- return {
391
- ...comment,
392
- replies: updatedReplies,
393
- };
394
- }
395
- else {
396
- return {
397
- ...comment,
398
- isLiked: !comment.isLiked,
399
- reactionsCount: comment.isLiked ? --comment.reactionsCount : ++comment.reactionsCount,
400
- };
401
- }
402
- }
403
- return comment;
1540
+ handleOnAction(e) {
1541
+ console.log('Action Triggered:', e);
1542
+ }
1543
+ scrollToEnd() {
1544
+ const conversationView = document.querySelector('ax-conversation-view');
1545
+ if (conversationView) {
1546
+ conversationView.scrollTo({
1547
+ top: conversationView.scrollHeight,
1548
+ behavior: 'smooth',
404
1549
  });
405
- });
1550
+ }
406
1551
  }
407
- removeMessageById(commentId, replyId) {
408
- this.comments.update((commentsList) => commentsList
409
- .map((comment) => {
410
- if (comment.id === commentId) {
411
- if (replyId) {
412
- return {
413
- ...comment,
414
- replies: comment.replies?.filter((reply) => reply.id !== replyId),
415
- };
1552
+ addMockMessage() { }
1553
+ async loadMessages(roomId) {
1554
+ try {
1555
+ // Get messages from the chat service
1556
+ const messageResponse = await this.chatService.getChatMessages(roomId);
1557
+ // Convert to chat preview format
1558
+ const chatPreviewMessages = convertToChatPreview(messageResponse);
1559
+ // Set the messages in the conversation service
1560
+ this.conversationService.chats.set(chatPreviewMessages);
1561
+ }
1562
+ catch (error) {
1563
+ console.error('Failed to load messages:', error);
1564
+ // Fallback to demo data if there's an error
1565
+ this.conversationService.chats.set(generateDemoChatPreview());
1566
+ }
1567
+ }
1568
+ loadDemoMessages() {
1569
+ this.conversationService.chats.set(generateDemoChatPreview());
1570
+ }
1571
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatPreviewComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
1572
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AXMChatPreviewComponent, isStandalone: false, selector: "axm-chat-preview", ngImport: i0, template: "<div class=\"ax-flex ax-flex-col ax-bg-surface-container\">\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-flex-1 ax-overflow-hidden\">\n <ax-conversation-view (onAction)=\"handleOnAction($event)\" [style.height]=\"dynamicHeight()\"></ax-conversation-view>\n </ax-conversation-container>\n\n <!-- Input Area -->\n <div class=\"ax-p-2\">\n <ax-conversation-input\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n ></ax-conversation-input>\n </div>\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;background-color:var(--ax-surface-container)}:host::ng-deep ax-conversation-view>div{height:50rem!important}\n"], dependencies: [{ 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: "component", type: i2$2.AXConversationViewComponent, selector: "ax-conversation-view", inputs: ["height", "isReply"], outputs: ["onScrollEnd", "onAction"] }, { kind: "component", type: i2$2.AXConversationInputComponent, selector: "ax-conversation-input", inputs: ["look", "placeholder", "maxLength", "hasAttachment", "hasVoice", "hasEmoji", "isLoading", "acceptFileType"], outputs: ["onSendClick", "onStartRecording", "onCancelRecording", "onEnterPressed"] }, { kind: "component", type: i2$2.AXConversationContainerComponent, selector: "ax-conversation-container" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1573
+ }
1574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatPreviewComponent, decorators: [{
1575
+ type: Component,
1576
+ args: [{ selector: 'axm-chat-preview', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ax-flex ax-flex-col ax-bg-surface-container\">\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-flex-1 ax-overflow-hidden\">\n <ax-conversation-view (onAction)=\"handleOnAction($event)\" [style.height]=\"dynamicHeight()\"></ax-conversation-view>\n </ax-conversation-container>\n\n <!-- Input Area -->\n <div class=\"ax-p-2\">\n <ax-conversation-input\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n ></ax-conversation-input>\n </div>\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;background-color:var(--ax-surface-container)}:host::ng-deep ax-conversation-view>div{height:50rem!important}\n"] }]
1577
+ }], ctorParameters: () => [{ type: i0.ElementRef }] });
1578
+
1579
+ /**
1580
+ * Abstract Comment Service defining the contract for comment functionality
1581
+ */
1582
+ class AXMCommentService {
1583
+ }
1584
+ class AXMCommentServiceImpl {
1585
+ constructor() {
1586
+ this.roomService = inject(AXMRoomService);
1587
+ this.messageService = inject(AXMMessageService);
1588
+ this.sessionService = inject(AXPSessionService);
1589
+ this.usersService = inject(AXMUsersEntityService);
1590
+ }
1591
+ // === Helper Methods ===
1592
+ getCurrentUser() {
1593
+ const user = this.sessionService.user;
1594
+ return {
1595
+ id: user?.id || 'guest-user',
1596
+ type: 'user',
1597
+ };
1598
+ }
1599
+ async getUserInfo(userId) {
1600
+ try {
1601
+ const user = await this.usersService.getOne(userId);
1602
+ return {
1603
+ id: user.id,
1604
+ userName: user.username || user.id,
1605
+ firstName: user.firstName || '',
1606
+ lastName: user.lastName || '',
1607
+ fullName: `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'Unknown User',
1608
+ avatar: '',
1609
+ };
1610
+ }
1611
+ catch (error) {
1612
+ console.error(`Failed to get user info for ID: ${userId}`, error);
1613
+ return {
1614
+ id: userId,
1615
+ userName: userId,
1616
+ firstName: 'Unknown',
1617
+ lastName: 'User',
1618
+ fullName: 'Unknown User',
1619
+ avatar: '',
1620
+ };
1621
+ }
1622
+ }
1623
+ formatRoomId(entityId, instanceId, roomType = 'default') {
1624
+ return `${roomType}:${entityId}:${instanceId}`;
1625
+ }
1626
+ async formatComment(message) {
1627
+ const currentUserId = this.getCurrentUser().id;
1628
+ // Check if the current user has liked this message
1629
+ const isLiked = (message.reactions || []).some((reaction) => reaction.author.id === currentUserId && reaction.type === 'like');
1630
+ // Count total reactions
1631
+ const reactionsCount = (message.reactions || []).filter((r) => r.type === 'like').length;
1632
+ // Get the full user info for the author
1633
+ const userInfo = await this.getUserInfo(message.author.id);
1634
+ return {
1635
+ ...message,
1636
+ author: {
1637
+ ...message.author,
1638
+ ...userInfo,
1639
+ },
1640
+ isFromCurrentUser: message.author.id === currentUserId,
1641
+ formattedDate: new Date(message.createdAt || new Date()).toLocaleString(),
1642
+ isLiked,
1643
+ reactionsCount,
1644
+ replies: [],
1645
+ };
1646
+ }
1647
+ async buildCommentHierarchy(comments) {
1648
+ // Create a map for quick lookup of comments by ID
1649
+ const commentMap = new Map();
1650
+ const rootComments = [];
1651
+ // First pass: add all comments to the map
1652
+ for (const comment of comments) {
1653
+ commentMap.set(comment.id, comment);
1654
+ // Initialize the replies array if not already present
1655
+ if (!comment.replies) {
1656
+ comment.replies = [];
1657
+ }
1658
+ // If this is a root comment (no replyId), add it to the root comments array
1659
+ if (!comment.replyId) {
1660
+ rootComments.push(comment);
1661
+ }
1662
+ }
1663
+ // Second pass: build the hierarchy by adding replies to their parent comments
1664
+ for (const comment of comments) {
1665
+ if (comment.replyId) {
1666
+ const parentComment = commentMap.get(comment.replyId);
1667
+ if (parentComment && parentComment.replies) {
1668
+ parentComment.replies.push(comment);
416
1669
  }
417
- return null;
1670
+ else {
1671
+ // If parent not found (might be deleted), treat as a root comment
1672
+ rootComments.push(comment);
1673
+ }
1674
+ }
1675
+ }
1676
+ // Sort the root comments and their replies by creation date (newest first)
1677
+ const sortByDate = (a, b) => {
1678
+ const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
1679
+ const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
1680
+ return dateB - dateA;
1681
+ };
1682
+ rootComments.sort(sortByDate);
1683
+ // Sort the replies within each comment
1684
+ for (const comment of rootComments) {
1685
+ if (comment.replies && comment.replies.length > 0) {
1686
+ comment.replies.sort(sortByDate);
418
1687
  }
419
- return comment;
420
- })
421
- .filter((comment) => comment !== null));
1688
+ }
1689
+ return rootComments;
422
1690
  }
423
- async submitComment(isPrivate = false) {
424
- if (!this.validateContent(this.commentContent()).result) {
425
- return;
1691
+ // === Room Operations ===
1692
+ async getRoomList(skip = 0, take = 100) {
1693
+ return this.roomService.query({ skip, take });
1694
+ }
1695
+ async getRoomDetails(roomId) {
1696
+ return this.roomService.getOne(roomId);
1697
+ }
1698
+ async createRoom(roomData) {
1699
+ return this.roomService.insertOne(roomData);
1700
+ }
1701
+ async ensureRoomExists(entityId, instanceId, roomType = 'default') {
1702
+ const roomId = this.formatRoomId(entityId, instanceId, roomType);
1703
+ try {
1704
+ // Check if room exists
1705
+ await this.getRoomDetails(roomId);
1706
+ return roomId;
426
1707
  }
427
- let memberLookup;
428
- if (isPrivate) {
429
- const popupConfig = {
430
- header: true,
431
- size: 'md',
432
- draggable: true,
433
- hasBackdrop: true,
434
- closeButton: true,
435
- closeOnBackdropClick: false,
1708
+ catch (error) {
1709
+ // If room doesn't exist, create it
1710
+ const roomData = {
1711
+ id: roomId, // Provide the ID directly for comments
1712
+ title: `Comments for ${entityId}:${instanceId}`,
1713
+ members: [this.getCurrentUser()],
436
1714
  };
437
- const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
438
- memberLookup = popup.data?.data?.lookup;
1715
+ // Create a new room or return the ID if it already exists
1716
+ try {
1717
+ const result = await this.createRoom(roomData);
1718
+ return typeof result === 'string' ? result : roomId;
1719
+ }
1720
+ catch (e) {
1721
+ // If we get a duplicate error, the room was created by another request
1722
+ return roomId;
1723
+ }
1724
+ }
1725
+ }
1726
+ // === Comment Operations ===
1727
+ async query(queryRequest) {
1728
+ const { params, skip = 0, take = 20 } = queryRequest;
1729
+ const { entityId, instanceId, roomType = 'default' } = params || {};
1730
+ if (!entityId || !instanceId) {
1731
+ throw new Error('EntityId and InstanceId are required for querying comments');
439
1732
  }
440
- if (this.isEditingMode()) {
441
- const payload = {
442
- content: this.commentContent(),
1733
+ const roomId = this.formatRoomId(entityId, instanceId, roomType);
1734
+ // Fetch messages for this room
1735
+ const result = await this.messageService.query({
1736
+ skip,
1737
+ take,
1738
+ filter: {
1739
+ field: 'roomId',
1740
+ value: roomId,
1741
+ operator: { type: 'equal' },
1742
+ },
1743
+ });
1744
+ // Transform messages to comments
1745
+ const comments = await Promise.all(result.items.map((message) => this.formatComment(message)));
1746
+ // Build comment hierarchy
1747
+ const hierarchicalComments = await this.buildCommentHierarchy(comments);
1748
+ return {
1749
+ items: hierarchicalComments,
1750
+ total: result.total,
1751
+ };
1752
+ }
1753
+ async getOne(id) {
1754
+ const message = await this.messageService.getOne(id);
1755
+ return this.formatComment(message);
1756
+ }
1757
+ async insertOne(commentData) {
1758
+ const { entityId, instanceId, roomType = 'default', content, contentType = 'text', replyId, isPrivate, } = commentData;
1759
+ // Ensure the room exists
1760
+ const roomId = await this.ensureRoomExists(entityId, instanceId, roomType);
1761
+ // Create the message content
1762
+ const messageContent = {
1763
+ content,
1764
+ contentType,
1765
+ };
1766
+ // Create the message object
1767
+ const message = {
1768
+ roomId,
1769
+ message: messageContent,
1770
+ author: this.getCurrentUser(),
1771
+ replyId: replyId || null,
1772
+ };
1773
+ // Insert the message
1774
+ const messageId = await this.messageService.insertOne(message);
1775
+ // Fetch the full message to return as a comment entity
1776
+ const insertedMessage = await this.messageService.getOne(messageId);
1777
+ return this.formatComment(insertedMessage);
1778
+ }
1779
+ async updateOne(id, update) {
1780
+ // If content is being updated, format it correctly
1781
+ let messageUpdate = {};
1782
+ if (update.message?.content) {
1783
+ messageUpdate.message = {
1784
+ content: update.message.content,
1785
+ contentType: update.message.contentType || 'text',
443
1786
  };
444
- //todo why ? need not nullable id !-!
445
- await this.commentService.updateOne(this.activeEditComment()?.id, payload);
446
- this.isSubmitting.set(true);
447
- this.hasCooldown.set(true);
448
- this.isEditingMode.set(false);
449
- this.activeEditComment.set(undefined);
1787
+ }
1788
+ // Apply the update
1789
+ const result = await this.messageService.updateOne(id, messageUpdate);
1790
+ // Return as a comment entity
1791
+ return this.formatComment(result);
1792
+ }
1793
+ async deleteOne(id) {
1794
+ try {
1795
+ await this.messageService.deleteOne(id);
1796
+ return true;
1797
+ }
1798
+ catch (error) {
1799
+ console.error('Failed to delete comment:', error);
1800
+ return false;
1801
+ }
1802
+ }
1803
+ // === Comment-Specific Operations ===
1804
+ async like(messageId) {
1805
+ const author = this.getCurrentUser();
1806
+ const message = await this.messageService.getOne(messageId);
1807
+ // Check if the user has already liked this message
1808
+ const reactions = message.reactions || [];
1809
+ const existingReaction = reactions.find((r) => r.author.id === author.id && r.type === 'like');
1810
+ let updatedReactions;
1811
+ if (existingReaction) {
1812
+ // If the reaction exists, remove it (unlike)
1813
+ updatedReactions = reactions.filter((r) => !(r.author.id === author.id && r.type === 'like'));
450
1814
  }
451
1815
  else {
452
- const payload = {
453
- ...this.payload(),
454
- content: this.commentContent(),
455
- contentType: 'text',
456
- isPrivate: isPrivate,
457
- replyId: this.activeReplyComment()?.id ?? null,
458
- //members: isPrivate ? [memberLookup] : [],
459
- };
460
- await this.commentService.insertOne(payload);
461
- this.isSubmitting.set(true);
462
- this.hasCooldown.set(true);
463
- this.isReplyingMode.set(false);
464
- this.activeReplyComment.set(undefined);
465
- }
466
- const response = await this.commentService.query(this.getPayload());
467
- this.comments.set(response.items);
468
- this.commentContent.set('');
469
- document.getElementsByClassName('ql-editor')[0].innerHTML = '';
470
- this.isSubmitting.set(false);
471
- setTimeout(() => {
472
- this.hasCooldown.set(false);
473
- }, 1000);
1816
+ // If the reaction doesn't exist, add it (like)
1817
+ updatedReactions = [...reactions, { author, type: 'like' }];
1818
+ }
1819
+ // Update the message with the new reactions
1820
+ const updatedMessage = await this.messageService.updateOne(messageId, { reactions: updatedReactions });
1821
+ // Return as a comment entity
1822
+ return this.formatComment(updatedMessage);
474
1823
  }
475
- scrollMain() {
476
- console.log('clicked');
477
- //
478
- const comment = this.isReplyingMode() ? this.activeReplyComment() : this.activeEditComment();
479
- const el = document.getElementById(comment.id);
480
- if (el) {
481
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
482
- const content = el?.firstElementChild?.children[1];
483
- const prevBg = content.style.background;
484
- content.style.borderRadius = '0.25rem';
485
- content.style.transition = 'background 1s ease-in-out';
486
- content.style.background = `rgba(var(--ax-color-on-surface), var(--tw-bg-opacity))`;
487
- setTimeout(() => {
488
- content.style.background = prevBg || 'rgba(0, 0, 0, 0)';
489
- }, 1000);
1824
+ async getCommentCount(entityId, instanceId, roomType = 'default') {
1825
+ const roomId = this.formatRoomId(entityId, instanceId, roomType);
1826
+ try {
1827
+ const result = await this.messageService.query({
1828
+ skip: 0,
1829
+ take: 1,
1830
+ filter: {
1831
+ field: 'roomId',
1832
+ value: roomId,
1833
+ operator: { type: 'equal' },
1834
+ },
1835
+ });
1836
+ return result.total;
1837
+ }
1838
+ catch (error) {
1839
+ console.error('Failed to get comment count:', error);
1840
+ return 0;
490
1841
  }
491
1842
  }
492
- calcDefrenetTime(date) {
493
- return date ? Date.now() - date.getTime() : undefined;
1843
+ async getAllCommentsWithReplies(entityId, instanceId, roomType = 'default', skip = 0, take = 100) {
1844
+ const queryRequest = {
1845
+ params: { entityId, instanceId, roomType },
1846
+ skip,
1847
+ take,
1848
+ };
1849
+ return this.query(queryRequest);
494
1850
  }
495
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
496
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMCommentListViewComponent, isStandalone: false, selector: "axm-comment-list-view", viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }, { propertyName: "wysiwyg", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-mt-2\">\n <ax-comment-container>\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 [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if(checkImageExists(comment.id!) && comment.user && comment.user.picture){\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"comment.user!.picture\">\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{\n extractInitials(comment.user?.firstName + ' ' + comment.user?.lastName)\n }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ comment.user?.firstName + ' ' + comment.user?.lastName }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\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)\">\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.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for(reply of comment.replies; track reply.id){\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if(reply && checkImageExists(reply.id!) && reply.user && reply.user.picture){\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"reply.user!.picture\">\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{\n extractInitials(reply.user?.firstName + ' ' + reply.user?.lastName)\n }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ (reply?.user?.firstName ?? '') + ' ' + (reply?.user?.lastName ?? '') }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\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)=\"deleteReply(comment, reply)\">\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.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n } </ax-comment-view>} @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 class=\"ax-mx-auto\" xmlns=\"http://www.w3.org/2000/svg\" width=\"154\" height=\"161\" viewBox=\"0 0 154 161\"\n fill=\"none\">\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 <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\" stroke=\"#E5E7EB\" />\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 <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 <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\" stroke=\"#818CF8\" />\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 <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 <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 <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 <ax-form>\n <ax-form-field>\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-bg-on-surface ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\">\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i [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\"></i>\n <div\n 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 {{\n isReplyingMode()\n ? activeReplyComment()?.user?.firstName + ' ' + activeReplyComment()?.user?.lastName\n : ''\n }}\n </span>\n </p>\n <div class=\"ax-truncate\" [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.content ?? '')\n : sanitizeHtml(activeEditComment()?.content ?? '')\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-toolbar>\n <ax-content>\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-content>\n <ax-suffix>\n <ax-dropdown-button [disabled]=\"hasCooldown()\" type=\"submit\" color=\"primary\" mode=\"split\" text=\"Send\"\n (onClick)=\"submitComment()\">\n @if(isSubmitting()){ <ax-loading></ax-loading> }\n <ax-button-item-list>\n <ax-button-item (click)=\"submitComment(true)\" 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-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>", styles: ["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}\n"], dependencies: [{ kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i3.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i3.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i3.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i3.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i3.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i3.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i3.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "component", type: i4.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: i4.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i4.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "component", type: i2$2.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "component", type: i1$1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "component", type: i7.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i7.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i7.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "component", type: i5.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { 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: i10.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "component", type: i11.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "component", type: i12.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: i13.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i13.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i13.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i13.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i13.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i13.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i13.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "pipe", type: i14.AsyncPipe, name: "async" }, { kind: "pipe", type: i15.AXFormatPipe, name: "format" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
497
- }
498
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentListViewComponent, decorators: [{
499
- type: Component,
500
- args: [{ selector: 'axm-comment-list-view', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-mt-2\">\n <ax-comment-container>\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 [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if(checkImageExists(comment.id!) && comment.user && comment.user.picture){\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"comment.user!.picture\">\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{\n extractInitials(comment.user?.firstName + ' ' + comment.user?.lastName)\n }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ comment.user?.firstName + ' ' + comment.user?.lastName }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\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)\">\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.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for(reply of comment.replies; track reply.id){\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if(reply && checkImageExists(reply.id!) && reply.user && reply.user.picture){\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"reply.user!.picture\">\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{\n extractInitials(reply.user?.firstName + ' ' + reply.user?.lastName)\n }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ (reply?.user?.firstName ?? '') + ' ' + (reply?.user?.lastName ?? '') }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\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)=\"deleteReply(comment, reply)\">\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.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n } </ax-comment-view>} @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 class=\"ax-mx-auto\" xmlns=\"http://www.w3.org/2000/svg\" width=\"154\" height=\"161\" viewBox=\"0 0 154 161\"\n fill=\"none\">\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 <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\" stroke=\"#E5E7EB\" />\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 <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 <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\" stroke=\"#818CF8\" />\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 <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 <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 <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 <ax-form>\n <ax-form-field>\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-bg-on-surface ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\">\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i [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\"></i>\n <div\n 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 {{\n isReplyingMode()\n ? activeReplyComment()?.user?.firstName + ' ' + activeReplyComment()?.user?.lastName\n : ''\n }}\n </span>\n </p>\n <div class=\"ax-truncate\" [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.content ?? '')\n : sanitizeHtml(activeEditComment()?.content ?? '')\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-toolbar>\n <ax-content>\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-content>\n <ax-suffix>\n <ax-dropdown-button [disabled]=\"hasCooldown()\" type=\"submit\" color=\"primary\" mode=\"split\" text=\"Send\"\n (onClick)=\"submitComment()\">\n @if(isSubmitting()){ <ax-loading></ax-loading> }\n <ax-button-item-list>\n <ax-button-item (click)=\"submitComment(true)\" 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-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>", styles: ["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}\n"] }]
501
- }] });
502
-
503
- class AXMCommentModule {
504
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
505
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentModule, declarations: [AXMCommentListViewComponent], imports: [FormsModule,
506
- CommonModule,
507
- RouterModule,
508
- AXDecoratorModule,
509
- AXWysiwygModule,
510
- AXButtonModule,
511
- AXTextBoxModule,
512
- AXLabelModule,
513
- AXAvatarModule,
514
- AXImageModule,
515
- AXSelectBoxModule,
516
- AXFormModule,
517
- AXLoadingModule,
518
- AXDropdownButtonModule,
519
- AXDropdownModule,
520
- AXFormatModule,
521
- AXToolBarModule,
522
- AXSkeletonModule,
523
- AXCommentModule], exports: [AXMCommentListViewComponent] }); }
524
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentModule, providers: [
525
- {
526
- provide: AXMCommentService,
527
- useClass: AXMCommentServiceImpl,
528
- },
529
- ], imports: [FormsModule,
530
- CommonModule,
531
- RouterModule,
532
- AXDecoratorModule,
533
- AXWysiwygModule,
534
- AXButtonModule,
535
- AXTextBoxModule,
536
- AXLabelModule,
537
- AXAvatarModule,
538
- AXImageModule,
539
- AXSelectBoxModule,
540
- AXFormModule,
541
- AXLoadingModule,
542
- AXDropdownButtonModule,
543
- AXDropdownModule,
544
- AXFormatModule,
545
- AXToolBarModule,
546
- AXSkeletonModule,
547
- AXCommentModule] }); }
1851
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1852
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentServiceImpl, providedIn: 'root' }); }
548
1853
  }
549
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentModule, decorators: [{
550
- type: NgModule,
1854
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentServiceImpl, decorators: [{
1855
+ type: Injectable,
551
1856
  args: [{
552
- declarations: [AXMCommentListViewComponent],
553
- imports: [
554
- FormsModule,
555
- CommonModule,
556
- RouterModule,
557
- AXDecoratorModule,
558
- AXWysiwygModule,
559
- AXButtonModule,
560
- AXTextBoxModule,
561
- AXLabelModule,
562
- AXAvatarModule,
563
- AXImageModule,
564
- AXSelectBoxModule,
565
- AXFormModule,
566
- AXLoadingModule,
567
- AXDropdownButtonModule,
568
- AXDropdownModule,
569
- AXFormatModule,
570
- AXToolBarModule,
571
- AXSkeletonModule,
572
- AXCommentModule,
573
- ],
574
- exports: [AXMCommentListViewComponent],
575
- providers: [
576
- {
577
- provide: AXMCommentService,
578
- useClass: AXMCommentServiceImpl,
579
- },
580
- ],
1857
+ providedIn: 'root',
581
1858
  }]
582
1859
  }] });
583
1860
 
584
- class AXMChatService extends AXMEntityCrudServiceImpl {
585
- }
586
- class AXMChatServiceImpl extends AXMChatService {
587
- markChatAsRead(roomId) {
588
- throw new Error('Method not implemented.');
589
- }
590
- async getTotalUnread() {
591
- throw new Error('Method not implemented.');
1861
+ class AXMCommentLookupPopup extends AXBasePageComponent {
1862
+ constructor() {
1863
+ super(...arguments);
1864
+ this.lookupNode = {
1865
+ name: 'lookup',
1866
+ path: 'lookup',
1867
+ type: 'lookup-editor',
1868
+ options: {
1869
+ entity: 'axoidc.users',
1870
+ },
1871
+ };
1872
+ this.context = {};
592
1873
  }
593
- constructor() {
594
- super(`${AXMConverstionModuleConst.moduleName}.${AXMConverstionModuleConst.chatName}`);
1874
+ handleClose() {
1875
+ this.close({
1876
+ result: true,
1877
+ data: this.context,
1878
+ });
595
1879
  }
596
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
597
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatServiceImpl }); }
598
- }
599
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatServiceImpl, decorators: [{
600
- type: Injectable
601
- }], ctorParameters: () => [] });
1880
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentLookupPopup, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1881
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AXMCommentLookupPopup, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `<axp-widgets-container
1882
+ class="ax-flex ax-flex-col ax-gap-2 ax-p-4"
1883
+ [context]="context"
1884
+ (onContextChanged)="context = $event.data"
1885
+ >
1886
+ <div class="ax-m-5">
1887
+ <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
1888
+ </div>
1889
+ </axp-widgets-container>
602
1890
 
603
- class AXMChatItemFooterComponent {
604
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
605
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AXMChatItemFooterComponent, isStandalone: false, selector: "axm-chat-item-footer", ngImport: i0, template: "<div class=\"ax-p-4\">\n <ax-button class=\"ax-w-full\" color=\"primary\" text=\"New Conversation\">\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", styles: [""], dependencies: [{ kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i4.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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1891
+ <ax-footer>
1892
+ <ax-suffix>
1893
+ <ax-button text="Accept & Send" color="primary" (onClick)="handleClose()"></ax-button>
1894
+ </ax-suffix>
1895
+ </ax-footer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: 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: AXPLayoutBuilderModule }, { kind: "component", type: i2$3.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i2$3.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i6$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"] }] }); }
606
1896
  }
607
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemFooterComponent, decorators: [{
1897
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentLookupPopup, decorators: [{
608
1898
  type: Component,
609
- args: [{ selector: 'axm-chat-item-footer', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ax-p-4\">\n <ax-button class=\"ax-w-full\" color=\"primary\" text=\"New Conversation\">\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" }]
1899
+ args: [{
1900
+ template: `<axp-widgets-container
1901
+ class="ax-flex ax-flex-col ax-gap-2 ax-p-4"
1902
+ [context]="context"
1903
+ (onContextChanged)="context = $event.data"
1904
+ >
1905
+ <div class="ax-m-5">
1906
+ <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
1907
+ </div>
1908
+ </axp-widgets-container>
1909
+
1910
+ <ax-footer>
1911
+ <ax-suffix>
1912
+ <ax-button text="Accept & Send" color="primary" (onClick)="handleClose()"></ax-button>
1913
+ </ax-suffix>
1914
+ </ax-footer>`,
1915
+ imports: [AXDecoratorModule, AXPLayoutBuilderModule, AXButtonModule],
1916
+ }]
610
1917
  }] });
611
1918
 
612
- class AXMChatItemHeaderComponent {
1919
+ class AXMCommentListViewComponent {
613
1920
  constructor() {
614
- this.total = input.required();
615
- this.unread = input.required();
616
- this.tab = viewChild.required('tab');
617
- this.tabsState = signal(0);
618
- this.activeTab = output();
619
- afterNextRender(() => {
620
- this.tab().onActiveTabChanged.subscribe((i) => {
621
- this.tabsState.set(i.index);
622
- this.activeTab.emit(i.index);
623
- });
1921
+ this.hasCooldown = signal(false);
1922
+ this.commentContent = signal('');
1923
+ this.isSubmitting = signal(false);
1924
+ this.isReplyingMode = signal(false);
1925
+ this.isEditingMode = signal(false);
1926
+ this.isLoading = signal(true);
1927
+ this.failedImageIds = signal([]);
1928
+ this.activeReplyComment = signal(undefined);
1929
+ this.activeEditComment = signal(undefined);
1930
+ this.wysiwygEditor = viewChild.required('w');
1931
+ this.commentService = inject(AXMCommentService);
1932
+ this.platform = inject(AXPlatform);
1933
+ this.route = inject(ActivatedRoute);
1934
+ this.popupService = inject(AXPopupService);
1935
+ this.toastService = inject(AXToastService);
1936
+ this.dialogService = inject(AXDialogService);
1937
+ this.sanitize = inject(DomSanitizer);
1938
+ this.routeParams = this.route.snapshot;
1939
+ this.getPayload = computed(() => ({
1940
+ params: this.payload(),
1941
+ skip: 0,
1942
+ take: 10,
1943
+ }));
1944
+ this.payload = computed(() => ({
1945
+ roomType: 'default',
1946
+ entityId: this.routeParams.params?.['module'] + '.' + this.routeParams.params?.['entity'],
1947
+ instanceId: this.routeParams.params?.['id'],
1948
+ }));
1949
+ this.comments = signal([]);
1950
+ this.wysiwyg = viewChild('w');
1951
+ this.wysiwygOptions = signal({
1952
+ look: 'solid',
1953
+ });
1954
+ this.avatarConfig = signal({
1955
+ color: 'primary',
1956
+ look: 'rounded',
1957
+ type: 'solid', // 'image' | 'text' | 'icon' | 'default'
624
1958
  });
1959
+ this.validateContent = (content) => {
1960
+ let isValid = true;
1961
+ if (!content || content === '<p><br></p>') {
1962
+ isValid = false;
1963
+ }
1964
+ return {
1965
+ rule: 'callback',
1966
+ result: isValid,
1967
+ message: isValid ? '' : 'Please fill the content',
1968
+ value: content,
1969
+ };
1970
+ };
625
1971
  }
626
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
627
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMChatItemHeaderComponent, isStandalone: false, selector: "axm-chat-item-header", inputs: { total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null }, unread: { classPropertyName: "unread", publicName: "unread", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { activeTab: "activeTab" }, viewQueries: [{ propertyName: "tab", first: true, predicate: ["tab"], descendants: true, isSignal: true }], ngImport: i0, template: "<div>\n <div class=\"ax-p-3\">\n <ax-text-box look=\"fill\" [placeholder]=\"'Search by username or email ...'\">\n <ax-prefix class=\"ax-ps-2\"> <ax-icon class=\"ax-text-neutral-400 ax-icon ax-icon-search\"> </ax-icon></ax-prefix\n ></ax-text-box>\n </div>\n <ax-divider></ax-divider>\n <ax-tabs #tab class=\"ax-text-neutral-400 ax-border-t\" [look]=\"'with-line'\">\n <ax-tab-item class=\"ax-tabs-fit !ax-py-5\" text=\"All\">\n <ax-suffix>\n <ax-badge [text]=\"total().toString()\" [color]=\"tabsState() === 0 ? 'primary' : 'secondary'\"></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n @if(unread()){\n <ax-tab-item class=\"ax-tabs-fit !ax-py-5\" text=\"Unread\">\n <ax-suffix>\n <ax-badge [text]=\"unread().toString()\" [color]=\"tabsState() === 1 ? 'primary' : 'secondary'\"></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n }\n </ax-tabs>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i3$1.AXTextBoxComponent, selector: "ax-text-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "maxLength", "allowNull", "type", "autoComplete", "look", "mask-options", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "component", type: i4$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i4$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
628
- }
629
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemHeaderComponent, decorators: [{
630
- type: Component,
631
- args: [{ selector: 'axm-chat-item-header', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div>\n <div class=\"ax-p-3\">\n <ax-text-box look=\"fill\" [placeholder]=\"'Search by username or email ...'\">\n <ax-prefix class=\"ax-ps-2\"> <ax-icon class=\"ax-text-neutral-400 ax-icon ax-icon-search\"> </ax-icon></ax-prefix\n ></ax-text-box>\n </div>\n <ax-divider></ax-divider>\n <ax-tabs #tab class=\"ax-text-neutral-400 ax-border-t\" [look]=\"'with-line'\">\n <ax-tab-item class=\"ax-tabs-fit !ax-py-5\" text=\"All\">\n <ax-suffix>\n <ax-badge [text]=\"total().toString()\" [color]=\"tabsState() === 0 ? 'primary' : 'secondary'\"></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n @if(unread()){\n <ax-tab-item class=\"ax-tabs-fit !ax-py-5\" text=\"Unread\">\n <ax-suffix>\n <ax-badge [text]=\"unread().toString()\" [color]=\"tabsState() === 1 ? 'primary' : 'secondary'\"></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n }\n </ax-tabs>\n</div>\n" }]
632
- }], ctorParameters: () => [] });
633
-
634
- class AXMChatItemComponent {
635
- constructor() {
636
- //input
637
- this.data = input.required();
638
- //output
639
- this.pressChatItem = output();
640
- //variables
641
- this.sessionService = inject(AXPSessionService);
642
- this.router = inject(Router);
643
- this.activatedRoute = inject(ActivatedRoute);
644
- this.fullName = computed(() => this.data().roomMembers[0].firstName + ' ' + this.data().roomMembers[0].lastName);
645
- this.myId = this.sessionService.user?.id;
1972
+ ngOnInit() {
1973
+ this.loadComments();
1974
+ }
1975
+ sanitizeHtml(htmlContent) {
1976
+ if (!htmlContent)
1977
+ return this.sanitize.bypassSecurityTrustHtml('');
1978
+ return this.sanitize.bypassSecurityTrustHtml(htmlContent);
1979
+ }
1980
+ handleImageError(imageId) {
1981
+ this.failedImageIds.update((ids) => [...ids, imageId]);
1982
+ }
1983
+ checkImageExists(imageId) {
1984
+ return !this.failedImageIds().includes(imageId);
646
1985
  }
647
- //methods
648
1986
  extractInitials(name) {
649
- const words = name.split(' ');
1987
+ if (!name)
1988
+ return '?';
1989
+ // Handle the case where name is an object with fullName property
1990
+ const nameStr = typeof name === 'object' && name.fullName ? name.fullName : String(name);
1991
+ const words = nameStr.split(' ');
650
1992
  const initials = words.map((word) => word.charAt(0).toUpperCase());
651
1993
  return initials.join('');
652
1994
  }
653
- async onPressChatItem(id) {
654
- this.pressChatItem.emit(id);
655
- await this.router.navigate([id], { relativeTo: this.activatedRoute });
656
- }
657
- messageSeenStatus() {
658
- if (this.myId === this.data().lastMessage.createdBy.id) {
659
- return '';
1995
+ async loadComments() {
1996
+ this.isLoading.set(true);
1997
+ try {
1998
+ const response = await this.commentService.query(this.getPayload());
1999
+ this.comments.set(response.items);
660
2000
  }
661
- else {
662
- if (this.data().lastMessage.hasSeen) {
663
- return 'ax-icon-dobble-check';
664
- }
665
- else {
666
- return 'ax-icon-check';
667
- }
2001
+ catch (error) {
2002
+ console.error('Failed to load comments:', error);
2003
+ this.toastService.show({
2004
+ content: 'Failed to load comments. Please try again.',
2005
+ color: 'danger',
2006
+ location: 'bottom-center',
2007
+ closeButton: true,
2008
+ timeOut: 3000,
2009
+ timeOutProgress: true,
2010
+ });
2011
+ this.comments.set([]);
2012
+ }
2013
+ finally {
2014
+ setTimeout(() => {
2015
+ this.isLoading.set(false);
2016
+ }, 250);
668
2017
  }
669
2018
  }
670
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
671
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMChatItemComponent, isStandalone: false, selector: "axm-chat-item", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { pressChatItem: "pressChatItem" }, ngImport: i0, template: "<div [id]=\"data().id\" class=\"ax-cursor-pointer hover:ax-bg-on-surface ax-flex ax-items-center ax-p-3 ax-justify-between\"\n (click)=\"onPressChatItem(data().id)\">\n <div class=\"ax-flex ax-min-w-[100px] ax-gap-3\">\n <div>\n <ax-avatar class=\"ax-relative\" color=\"primary\" shape=\"rounded\">\n <ax-badge class=\"ax-absolute ax-bottom-0 ax-right-0 !ax-size-3 ax-border !ax-border-white ax-rounded-full\"\n color=\"success\"></ax-badge>\n @if(data().roomMembers[0].picture){\n <ax-image [src]=\"data().roomMembers[0].picture\">\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(fullName()) }}</span>\n </ax-text>\n }\n </ax-avatar>\n </div>\n\n <div class=\"ax-py-1 ax-flex ax-flex-col ax-truncate\">\n <p class=\"ax-font-semibold ax-w-full ax-truncate ax-pb-1\">{{ fullName() }}</p>\n <p class=\"ax-text-sm ax-font-normal ax-text-neutral-400 ax-w-full ax-truncate\">\n {{ data().lastMessage.content }}\n </p>\n </div>\n </div>\n\n <div class=\"ax-flex ax-flex-col ax-items-end ax-gap-1\">\n <div class=\"ax-flex ax-items-center ax-justify-between\">\n @if(true) {\n <ax-icon class=\"ax-icon ax-text-success-500\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n <ax-text class=\"ax-text-sm ax-font-normal ax-text-neutral-400 ax-text-nowrap\">{{\n data().lastMessage.createdAt.toDateString()\n }}</ax-text>\n </div>\n <ax-badge [class]=\"data().unreadCount ? 'ax-visible' : 'ax-invisible'\" color=\"success\"\n [text]=\"data().unreadCount.toString()\"></ax-badge>\n </div>\n</div>\n<!-- <div class=\"ax-p-3\">\n <div class=\"ax-grid ax-grid-cols-12\">\n \n </div>\n</div> -->", styles: [".black{background-color:#000}\n"], dependencies: [{ kind: "component", type: i1$1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "component", type: i2$2.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i5.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
672
- }
673
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatItemComponent, decorators: [{
674
- type: Component,
675
- args: [{ selector: 'axm-chat-item', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div [id]=\"data().id\" class=\"ax-cursor-pointer hover:ax-bg-on-surface ax-flex ax-items-center ax-p-3 ax-justify-between\"\n (click)=\"onPressChatItem(data().id)\">\n <div class=\"ax-flex ax-min-w-[100px] ax-gap-3\">\n <div>\n <ax-avatar class=\"ax-relative\" color=\"primary\" shape=\"rounded\">\n <ax-badge class=\"ax-absolute ax-bottom-0 ax-right-0 !ax-size-3 ax-border !ax-border-white ax-rounded-full\"\n color=\"success\"></ax-badge>\n @if(data().roomMembers[0].picture){\n <ax-image [src]=\"data().roomMembers[0].picture\">\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(fullName()) }}</span>\n </ax-text>\n }\n </ax-avatar>\n </div>\n\n <div class=\"ax-py-1 ax-flex ax-flex-col ax-truncate\">\n <p class=\"ax-font-semibold ax-w-full ax-truncate ax-pb-1\">{{ fullName() }}</p>\n <p class=\"ax-text-sm ax-font-normal ax-text-neutral-400 ax-w-full ax-truncate\">\n {{ data().lastMessage.content }}\n </p>\n </div>\n </div>\n\n <div class=\"ax-flex ax-flex-col ax-items-end ax-gap-1\">\n <div class=\"ax-flex ax-items-center ax-justify-between\">\n @if(true) {\n <ax-icon class=\"ax-icon ax-text-success-500\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n <ax-text class=\"ax-text-sm ax-font-normal ax-text-neutral-400 ax-text-nowrap\">{{\n data().lastMessage.createdAt.toDateString()\n }}</ax-text>\n </div>\n <ax-badge [class]=\"data().unreadCount ? 'ax-visible' : 'ax-invisible'\" color=\"success\"\n [text]=\"data().unreadCount.toString()\"></ax-badge>\n </div>\n</div>\n<!-- <div class=\"ax-p-3\">\n <div class=\"ax-grid ax-grid-cols-12\">\n \n </div>\n</div> -->", styles: [".black{background-color:#000}\n"] }]
676
- }] });
677
-
678
- class AXMChatPreviewHeaderComponent {
679
- constructor() {
680
- this.pressBack = output();
2019
+ editMessage(comment, reply) {
2020
+ this.isReplyingMode.set(false);
2021
+ this.activeReplyComment.set(undefined);
2022
+ this.isEditingMode.set(true);
2023
+ this.activeEditComment.set(comment);
2024
+ const contentToEdit = reply ? reply.message?.content : comment.message?.content;
2025
+ this.commentContent.set(contentToEdit || '');
2026
+ this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
681
2027
  }
682
- emitBack() {
683
- this.pressBack.emit();
2028
+ replyMessage(comment, reply) {
2029
+ this.isEditingMode.set(false);
2030
+ this.activeEditComment.set(undefined);
2031
+ this.isReplyingMode.set(true);
2032
+ this.activeReplyComment.set(comment);
2033
+ if (reply) {
2034
+ const author = reply.author.fullName || reply.author.id;
2035
+ const mention = `<a data-id="${reply.id}">@${author}</a> `;
2036
+ this.commentContent.set(mention);
2037
+ this.wysiwyg()?.focus();
2038
+ document.getElementsByClassName('ql-editor')[0].innerHTML = mention;
2039
+ }
2040
+ this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
684
2041
  }
685
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatPreviewHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
686
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMChatPreviewHeaderComponent, isStandalone: false, selector: "axm-chat-preview-header", outputs: { pressBack: "pressBack" }, ngImport: i0, template: "<div class=\"ax-flex ax-justify-between ax-items-center ax-py-[0.61rem] ax-px-4 ax-border-b\">\n <div class=\"ax-flex ax-gap-5\">\n <div class=\"md:ax-hidden ax-flex ax-justify-center ax-items-center ax-text-xl\">\n <ax-button look=\"blank\" class=\"ax-md\" (onClick)=\"emitBack()\">\n <ax-icon> <i class=\"ax-text-neutral-400 fa-regular fa-chevron-left\"></i></ax-icon>\n </ax-button>\n </div>\n <ax-avatar class=\"ax-relative ax-shrink-0\" color=\"primary\" shape=\"rounded\">\n @if(false){\n <ax-image>\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\"></span>\n </ax-text>\n }\n </ax-avatar>\n <div>\n <div class=\"ax-text-xl\">Office Chat</div>\n <div class=\"ax-text-neutral-400 ax-text-xs\">23 members, 10 online</div>\n </div>\n </div>\n <div class=\"ax-flex ax-justify-start ax-items-center ax-gap-x-10 ax-text-xl ax-text-neutral-400\">\n <i class=\"fa-regular fa-phone-flip\"> </i>\n <i class=\"fa-regular fa-search\"> </i>\n <i class=\"fa-regular fa-ellipsis-vertical\"> </i>\n </div>\n</div>", styles: [""], dependencies: [{ kind: "component", type: i1$1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "component", type: i2$2.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i4.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.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
687
- }
688
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatPreviewHeaderComponent, decorators: [{
689
- type: Component,
690
- args: [{ selector: 'axm-chat-preview-header', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ax-flex ax-justify-between ax-items-center ax-py-[0.61rem] ax-px-4 ax-border-b\">\n <div class=\"ax-flex ax-gap-5\">\n <div class=\"md:ax-hidden ax-flex ax-justify-center ax-items-center ax-text-xl\">\n <ax-button look=\"blank\" class=\"ax-md\" (onClick)=\"emitBack()\">\n <ax-icon> <i class=\"ax-text-neutral-400 fa-regular fa-chevron-left\"></i></ax-icon>\n </ax-button>\n </div>\n <ax-avatar class=\"ax-relative ax-shrink-0\" color=\"primary\" shape=\"rounded\">\n @if(false){\n <ax-image>\n <ax-loading></ax-loading>\n </ax-image>\n }@else{\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\"></span>\n </ax-text>\n }\n </ax-avatar>\n <div>\n <div class=\"ax-text-xl\">Office Chat</div>\n <div class=\"ax-text-neutral-400 ax-text-xs\">23 members, 10 online</div>\n </div>\n </div>\n <div class=\"ax-flex ax-justify-start ax-items-center ax-gap-x-10 ax-text-xl ax-text-neutral-400\">\n <i class=\"fa-regular fa-phone-flip\"> </i>\n <i class=\"fa-regular fa-search\"> </i>\n <i class=\"fa-regular fa-ellipsis-vertical\"> </i>\n </div>\n</div>" }]
691
- }] });
692
-
693
- class AXMChatPreviewComponent {
694
- constructor(elRef) {
695
- this.elRef = elRef;
696
- // Signal for dynamic max-height (using computed logic for real-time updates)
697
- this.activatedRoute = inject(ActivatedRoute);
698
- this.fileService = inject(AXFileService);
699
- this.conversationService = inject(AXConversationService);
700
- this.options = signal({
701
- disabled: false,
702
- readonly: false,
703
- value: '',
704
- });
705
- this.conversationViewMaxHeight = signal('');
706
- this.initialTextAreaHeight = 0;
707
- this.textareaHeight = signal(0);
708
- this.dynamicHeight = computed(() => {
709
- const baseOffset = 275;
710
- const currentHeight = this.textareaHeight();
711
- const dynamicOffset = baseOffset + (currentHeight - this.initialTextAreaHeight);
712
- return `calc(100vh - ${dynamicOffset}px) !important`;
2042
+ async deleteComment(comment) {
2043
+ const dialog = this.dialogService.open({
2044
+ icon: 'fa-regular fa-warning',
2045
+ content: 'Are you sure you want to delete this comment?',
2046
+ title: 'Delete Comment',
2047
+ type: 'danger',
2048
+ orientation: 'horizontal',
2049
+ buttons: [
2050
+ {
2051
+ text: 'Delete',
2052
+ color: 'danger',
2053
+ onClick: async (e) => {
2054
+ e.handled = true;
2055
+ e.source.text = 'Deleting...';
2056
+ e.source.disabled = true;
2057
+ e.source.loading = true;
2058
+ try {
2059
+ if (comment.id) {
2060
+ await this.commentService.deleteOne(comment.id);
2061
+ this.removeMessageById(comment.id);
2062
+ this.toastService.show({
2063
+ content: 'Comment deleted successfully.',
2064
+ color: 'success',
2065
+ location: 'bottom-center',
2066
+ closeButton: true,
2067
+ timeOut: 3000,
2068
+ timeOutProgress: true,
2069
+ });
2070
+ if (this.isEditingMode() && this.activeEditComment()?.id === comment.id) {
2071
+ this.resetReplyEditState();
2072
+ }
2073
+ dialog.close();
2074
+ }
2075
+ }
2076
+ catch (error) {
2077
+ this.toastService.show({
2078
+ content: typeof error === 'string' ? error : 'Failed to delete comment!',
2079
+ color: 'danger',
2080
+ location: 'bottom-center',
2081
+ closeButton: true,
2082
+ timeOut: 3000,
2083
+ timeOutProgress: true,
2084
+ });
2085
+ }
2086
+ },
2087
+ },
2088
+ {
2089
+ text: 'Cancel',
2090
+ color: 'default',
2091
+ autofocus: true,
2092
+ onClick: (e) => {
2093
+ dialog.close();
2094
+ },
2095
+ },
2096
+ ],
2097
+ closeButton: false,
713
2098
  });
714
- afterNextRender(() => {
715
- const textareaContainer = this.elRef.nativeElement.querySelector('ax-conversation-input > div');
716
- if (textareaContainer) {
717
- this.initialTextAreaHeight = textareaContainer.offsetHeight;
718
- this.resizeObserver = new ResizeObserver((entries) => {
719
- for (const entry of entries) {
720
- if (entry.target === textareaContainer) {
721
- const currentHeight = entry.contentRect.height;
722
- this.textareaHeight.set(currentHeight);
2099
+ }
2100
+ async deleteReply(comment, reply) {
2101
+ const dialog = this.dialogService.open({
2102
+ icon: 'fa-regular fa-warning',
2103
+ content: 'Are you sure you want to delete this reply?',
2104
+ title: 'Delete Reply',
2105
+ type: 'danger',
2106
+ orientation: 'horizontal',
2107
+ buttons: [
2108
+ {
2109
+ text: 'Delete',
2110
+ color: 'danger',
2111
+ onClick: async (e) => {
2112
+ e.handled = true;
2113
+ e.source.text = 'Deleting...';
2114
+ e.source.disabled = true;
2115
+ e.source.loading = true;
2116
+ try {
2117
+ if (reply.id) {
2118
+ await this.commentService.deleteOne(reply.id);
2119
+ this.removeMessageById(comment.id, reply.id);
2120
+ this.toastService.show({
2121
+ content: 'Comment deleted successfully.',
2122
+ color: 'success',
2123
+ location: 'bottom-center',
2124
+ closeButton: true,
2125
+ timeOut: 3000,
2126
+ timeOutProgress: true,
2127
+ });
2128
+ dialog.close();
2129
+ }
2130
+ }
2131
+ catch (error) {
2132
+ this.toastService.show({
2133
+ content: typeof error === 'string' ? error : 'Failed to delete comment!',
2134
+ color: 'danger',
2135
+ location: 'bottom-center',
2136
+ closeButton: true,
2137
+ timeOut: 3000,
2138
+ timeOutProgress: true,
2139
+ });
723
2140
  }
724
- }
725
- });
726
- this.resizeObserver.observe(textareaContainer);
727
- }
728
- else {
729
- console.warn('Textarea Container element not found.');
730
- }
731
- this.activatedRoute.params.subscribe((params) => {
732
- console.log(params['id']);
733
- this.messageId = params['id'];
734
- this.conversationService.chats.set([
735
- {
736
- id: '0',
737
- sendTime: new Date(),
738
- type: 'text',
739
- readTime: new Date(),
740
- content: this.messageId,
741
- name: 'test name',
742
2141
  },
743
- ]);
744
- });
745
- });
746
- }
747
- ngOnDestroy() {
748
- if (this.resizeObserver) {
749
- this.resizeObserver.disconnect();
750
- }
751
- }
752
- handleFileChange(event) {
753
- console.log('File Changed:', event);
754
- }
755
- handleCancelRecord(event) {
756
- console.log('Recording Cancelled:', event);
757
- }
758
- handleEndRecord(event) {
759
- this.fileService.blobToBase64(event.data.value).then((c) => {
760
- this.conversationService.chats.update((values) => [
761
- ...values,
762
- {
763
- id: `${Math.floor(Math.random() * 100)}`,
764
- content: c,
765
- sendTime: new Date(),
766
- type: 'voice',
767
2142
  },
768
- ]);
769
- });
770
- }
771
- handleOnSend(e) {
772
- console.log('Message Sent:', e);
773
- if (e.data.value) {
774
- this.options.update((prev) => ({ ...prev, value: '' }));
775
- this.conversationService.chats.update((values) => [
776
- ...values,
777
2143
  {
778
- id: `${Math.floor(Math.random() * 100)}`,
779
- content: e.data.value,
780
- sendTime: new Date(),
781
- type: e.data.type,
782
- replyTo: e.data.replyChat,
783
- fromId: '10',
2144
+ text: 'Cancel',
2145
+ color: 'default',
2146
+ autofocus: true,
2147
+ onClick: (e) => {
2148
+ dialog.close();
2149
+ },
784
2150
  },
785
- ]);
786
- }
787
- this.scrollToEnd();
2151
+ ],
2152
+ closeButton: false,
2153
+ });
788
2154
  }
789
- handleOnAction(e) {
790
- console.log('Action Triggered:', e);
2155
+ resetReplyEditState() {
2156
+ this.isEditingMode.set(false);
2157
+ this.activeEditComment.set(undefined);
2158
+ this.isReplyingMode.set(false);
2159
+ this.activeReplyComment.set(undefined);
2160
+ this.commentContent.set('');
2161
+ document.getElementsByClassName('ql-editor')[0].innerHTML = '';
791
2162
  }
792
- scrollToEnd() {
793
- const conversationView = document.querySelector('ax-conversation-view');
794
- if (conversationView) {
795
- conversationView.scrollTo({
796
- top: conversationView.scrollHeight,
797
- behavior: 'smooth',
798
- });
2163
+ async toggleLike(comment, reply) {
2164
+ try {
2165
+ if (reply) {
2166
+ if (reply.id) {
2167
+ this.updateLikeStatus(comment.id, reply.id);
2168
+ await this.commentService.like(reply.id);
2169
+ }
2170
+ }
2171
+ else {
2172
+ if (comment.id) {
2173
+ this.updateLikeStatus(comment.id);
2174
+ await this.commentService.like(comment.id);
2175
+ }
2176
+ }
2177
+ }
2178
+ catch (error) {
2179
+ console.error('Failed to toggle like:', error);
2180
+ // Revert the optimistic update if the server call fails
2181
+ if (reply && reply.id) {
2182
+ this.updateLikeStatus(comment.id, reply.id);
2183
+ }
2184
+ else if (comment.id) {
2185
+ this.updateLikeStatus(comment.id);
2186
+ }
799
2187
  }
800
2188
  }
801
- addMockMessage() { }
802
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatPreviewComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
803
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AXMChatPreviewComponent, isStandalone: false, selector: "axm-chat-preview", ngImport: i0, template: "<ax-conversation-container>\n <ax-conversation-view (onAction)=\"handleOnAction($event)\" [style.height]=\"dynamicHeight()\"> </ax-conversation-view>\n <ax-conversation-input\n placeholder=\"placeholder\"\n [(ngModel)]=\"options().value\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n >\n </ax-conversation-input>\n</ax-conversation-container>\n", styles: ["ax-conversation-container>div.ax-conversation-container{padding:1rem!important}ax-conversation-container>div.ax-conversation-container ax-conversation-view{padding:1.5rem}\n"], dependencies: [{ kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2$3.AXConversationViewComponent, selector: "ax-conversation-view", inputs: ["height", "isReply"], outputs: ["onScrollEnd", "onAction"] }, { kind: "component", type: i2$3.AXConversationInputComponent, selector: "ax-conversation-input", inputs: ["look", "placeholder", "maxLength", "hasAttachment", "hasVoice", "hasEmoji", "isLoading", "acceptFileType"], outputs: ["onSendClick", "onStartRecording", "onCancelRecording", "onEnterPressed"] }, { kind: "component", type: i2$3.AXConversationContainerComponent, selector: "ax-conversation-container" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
804
- }
805
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatPreviewComponent, decorators: [{
806
- type: Component,
807
- args: [{ selector: 'axm-chat-preview', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ax-conversation-container>\n <ax-conversation-view (onAction)=\"handleOnAction($event)\" [style.height]=\"dynamicHeight()\"> </ax-conversation-view>\n <ax-conversation-input\n placeholder=\"placeholder\"\n [(ngModel)]=\"options().value\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n >\n </ax-conversation-input>\n</ax-conversation-container>\n", styles: ["ax-conversation-container>div.ax-conversation-container{padding:1rem!important}ax-conversation-container>div.ax-conversation-container ax-conversation-view{padding:1.5rem}\n"] }]
808
- }], ctorParameters: () => [{ type: i0.ElementRef }] });
809
-
810
- class AXMChatComponent {
811
- constructor() {
812
- this.chatService = inject(AXMChatService);
813
- this.route = inject(ActivatedRoute);
814
- this.routeParams = this.route.snapshot;
815
- this.data = signal(undefined);
816
- this.filteredData = computed(() => (this.data()?.items).filter((i) => i.unreadCount));
817
- this.displayData = computed(() => (this.activeTab() ? this.filteredData() : this.data()?.items));
818
- this.activeTab = signal(0);
819
- this.allCount = computed(() => this.data()?.total ?? 0);
820
- this.unreadCount = signal(0);
821
- }
822
- async ngOnInit() {
823
- await this.loadData();
2189
+ updateLikeStatus(commentId, replyId) {
2190
+ this.comments.update((commentsList) => {
2191
+ return commentsList.map((comment) => {
2192
+ if (comment.id === commentId) {
2193
+ if (replyId && comment.replies) {
2194
+ const updatedReplies = comment.replies.map((reply) => reply.id === replyId
2195
+ ? {
2196
+ ...reply,
2197
+ isLiked: !reply.isLiked,
2198
+ reactionsCount: reply.isLiked && reply.reactionsCount !== undefined
2199
+ ? reply.reactionsCount - 1
2200
+ : (reply.reactionsCount || 0) + 1,
2201
+ }
2202
+ : reply);
2203
+ return {
2204
+ ...comment,
2205
+ replies: updatedReplies,
2206
+ };
2207
+ }
2208
+ else {
2209
+ return {
2210
+ ...comment,
2211
+ isLiked: !comment.isLiked,
2212
+ reactionsCount: comment.isLiked && comment.reactionsCount !== undefined
2213
+ ? comment.reactionsCount - 1
2214
+ : (comment.reactionsCount || 0) + 1,
2215
+ };
2216
+ }
2217
+ }
2218
+ return comment;
2219
+ });
2220
+ });
824
2221
  }
825
- async loadData() {
826
- this.data.set(await this.chatService.query({
827
- params: {
828
- roomType: 'default',
829
- entityId: this.routeParams.params?.['module'] + '.' + this.routeParams.params?.['entity'],
830
- instanceId: this.routeParams.params?.['id'],
831
- },
832
- skip: 0,
833
- take: 10,
834
- }));
835
- this.unreadCount.set(await this.chatService.getTotalUnread());
836
- console.log(this.unreadCount());
2222
+ removeMessageById(commentId, replyId) {
2223
+ if (replyId) {
2224
+ // Remove a reply from a comment
2225
+ this.comments.update((commentsList) => commentsList.map((comment) => {
2226
+ if (comment.id === commentId && comment.replies) {
2227
+ return {
2228
+ ...comment,
2229
+ replies: comment.replies.filter((reply) => reply.id !== replyId),
2230
+ };
2231
+ }
2232
+ return comment;
2233
+ }));
2234
+ }
2235
+ else {
2236
+ // Remove a whole comment
2237
+ this.comments.update((commentsList) => commentsList.filter((comment) => comment.id !== commentId));
2238
+ }
837
2239
  }
838
- async markChatAsRead(roomId) {
839
- let oldUnreadCount;
2240
+ async submitComment(isPrivate = false) {
2241
+ if (!this.validateContent(this.commentContent()).result) {
2242
+ return;
2243
+ }
2244
+ this.isSubmitting.set(true);
2245
+ let memberLookup;
2246
+ if (isPrivate) {
2247
+ const popupConfig = {
2248
+ header: true,
2249
+ size: 'md',
2250
+ draggable: true,
2251
+ hasBackdrop: true,
2252
+ closeButton: true,
2253
+ closeOnBackdropClick: false,
2254
+ };
2255
+ const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
2256
+ memberLookup = popup.data?.data?.lookup;
2257
+ }
840
2258
  try {
841
- if (this.data()) {
842
- const targetRoom = this.data()?.items.find((i) => i.id === roomId && i.unreadCount);
843
- if (!targetRoom) {
844
- return;
845
- }
846
- oldUnreadCount = targetRoom.unreadCount;
847
- // Mark chat as read on the service
848
- await this.chatService.markChatAsRead(roomId);
849
- // Update the signal state
850
- this.data.update((old) => ({
851
- total: old.total,
852
- items: old.items.map((i) => (i.id === roomId ? { ...i, unreadCount: 0 } : i)),
853
- }));
854
- this.unreadCount.update((i) => --i);
2259
+ if (this.isEditingMode() && this.activeEditComment()?.id) {
2260
+ const payload = {
2261
+ message: {
2262
+ content: this.commentContent(),
2263
+ contentType: 'text',
2264
+ },
2265
+ };
2266
+ await this.commentService.updateOne(this.activeEditComment().id, payload);
2267
+ this.hasCooldown.set(true);
2268
+ this.isEditingMode.set(false);
2269
+ this.activeEditComment.set(undefined);
2270
+ }
2271
+ else {
2272
+ const payload = {
2273
+ ...this.payload(),
2274
+ content: this.commentContent(),
2275
+ contentType: 'text',
2276
+ isPrivate: isPrivate,
2277
+ replyId: this.activeReplyComment()?.id ?? null,
2278
+ };
2279
+ await this.commentService.insertOne(payload);
2280
+ this.hasCooldown.set(true);
2281
+ this.isReplyingMode.set(false);
2282
+ this.activeReplyComment.set(undefined);
855
2283
  }
2284
+ // Reload comments to get the updated list with proper hierarchy
2285
+ const response = await this.commentService.query(this.getPayload());
2286
+ this.comments.set(response.items);
2287
+ this.commentContent.set('');
2288
+ document.getElementsByClassName('ql-editor')[0].innerHTML = '';
2289
+ setTimeout(() => {
2290
+ this.hasCooldown.set(false);
2291
+ }, 1000);
856
2292
  }
857
2293
  catch (error) {
858
- // Roll back the update only if oldUnreadCount is defined
859
- if (oldUnreadCount !== undefined) {
860
- this.data.update((old) => ({
861
- total: old.total,
862
- items: old.items.map((i) => (i.id === roomId ? { ...i, unreadCount: oldUnreadCount } : i)),
863
- }));
2294
+ console.error('Error submitting comment:', error);
2295
+ this.toastService.show({
2296
+ content: typeof error === 'string' ? error : 'Failed to submit comment. Please try again.',
2297
+ color: 'danger',
2298
+ location: 'bottom-center',
2299
+ closeButton: true,
2300
+ timeOut: 3000,
2301
+ timeOutProgress: true,
2302
+ });
2303
+ }
2304
+ finally {
2305
+ this.isSubmitting.set(false);
2306
+ }
2307
+ }
2308
+ scrollMain() {
2309
+ const comment = this.isReplyingMode() ? this.activeReplyComment() : this.activeEditComment();
2310
+ if (comment && comment.id) {
2311
+ const el = document.getElementById(comment.id);
2312
+ if (el) {
2313
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
2314
+ const content = el?.firstElementChild?.children[1];
2315
+ if (content) {
2316
+ const prevBg = content.style.background;
2317
+ content.style.borderRadius = '0.25rem';
2318
+ content.style.transition = 'background 1s ease-in-out';
2319
+ content.style.background = `rgba(var(--ax-color-on-surface), var(--tw-bg-opacity))`;
2320
+ setTimeout(() => {
2321
+ content.style.background = prevBg || 'rgba(0, 0, 0, 0)';
2322
+ }, 1000);
2323
+ }
864
2324
  }
865
- console.error('Error marking chat as read:', error);
866
2325
  }
867
2326
  }
868
- setActiveTabData(event) {
869
- this.activeTab.set(event);
2327
+ calcDefrenetTime(date) {
2328
+ return date ? Date.now() - date.getTime() : undefined;
870
2329
  }
871
- goBack() { }
872
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
873
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMChatComponent, isStandalone: false, selector: "axm-chat", ngImport: i0, template: "<div class=\"ax-flex ax-h-full\">\n <div class=\"ax-w-full ax-border-e ax-hidden md:ax-block md:ax-w-2/5 xl:ax-w-1/4\" axResizable>\n <div class=\"ax-flex ax-flex-col ax-justify-between ax-h-full\">\n <div>\n <axm-chat-item-header\n (activeTab)=\"setActiveTabData($event)\"\n [unread]=\"unreadCount()\"\n [total]=\"allCount()\"\n ></axm-chat-item-header>\n <div class=\"ax-overflow-y-auto ax-max-h-[calc(100vh-303px)]\">\n @for(i of displayData() ; track i.id){\n <axm-chat-item [data]=\"i\" (click)=\"markChatAsRead(i.id)\"></axm-chat-item>\n }\n </div>\n </div>\n <div>\n <axm-chat-item-footer></axm-chat-item-footer>\n </div>\n </div>\n </div>\n <div class=\"ax-h-full md:ax-w-3/5 xl:ax-w-3/4 ax-relative\">\n <axm-chat-preview-header (pressBack)=\"goBack()\"></axm-chat-preview-header>\n <!-- <axm-chat-preview></axm-chat-preview> -->\n <router-outlet></router-outlet>\n \n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$3.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AXMChatItemComponent, selector: "axm-chat-item", inputs: ["data"], outputs: ["pressChatItem"] }, { kind: "component", type: AXMChatItemHeaderComponent, selector: "axm-chat-item-header", inputs: ["total", "unread"], outputs: ["activeTab"] }, { kind: "component", type: AXMChatItemFooterComponent, selector: "axm-chat-item-footer" }, { kind: "component", type: AXMChatPreviewHeaderComponent, selector: "axm-chat-preview-header", outputs: ["pressBack"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2330
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2331
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: AXMCommentListViewComponent, isStandalone: false, selector: "axm-comment-list-view", viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }, { propertyName: "wysiwyg", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-mt-2\">\n <ax-comment-container>\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 [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (checkImageExists(comment.id!) && comment.author && extractInitials(comment.author) !== '?') {\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(comment.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(comment.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\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)\">\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)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (reply && checkImageExists(reply.id!) && reply.author && extractInitials(reply.author) !== '?') {\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(reply.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(reply.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\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)=\"deleteReply(comment, reply)\">\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(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></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 <ax-form>\n <ax-form-field>\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-bg-on-surface 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\n class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\"\n >\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() ? extractInitials(activeReplyComment()?.author) : '' }}\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-toolbar>\n <ax-content>\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-content>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown()\"\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(true)\" 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-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>\n", styles: ["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}\n"], dependencies: [{ 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: "component", type: i2$4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i2$4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i2$4.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i2$4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i2$4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i2$4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i2$4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "component", type: i1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "component", type: i2.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { 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: "component", type: i6$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: i6$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i6$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "component", type: i5$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: i8.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i8.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i8.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i8.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i8.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i8.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i8.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "component", type: i9.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i9.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i9.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "component", type: i10$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: i11$1.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "component", type: i12.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "component", type: i13.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "pipe", type: i14.AsyncPipe, name: "async" }, { kind: "pipe", type: i15.AXFormatPipe, name: "format" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
874
2332
  }
875
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatComponent, decorators: [{
2333
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMCommentListViewComponent, decorators: [{
876
2334
  type: Component,
877
- args: [{ selector: 'axm-chat', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"ax-flex ax-h-full\">\n <div class=\"ax-w-full ax-border-e ax-hidden md:ax-block md:ax-w-2/5 xl:ax-w-1/4\" axResizable>\n <div class=\"ax-flex ax-flex-col ax-justify-between ax-h-full\">\n <div>\n <axm-chat-item-header\n (activeTab)=\"setActiveTabData($event)\"\n [unread]=\"unreadCount()\"\n [total]=\"allCount()\"\n ></axm-chat-item-header>\n <div class=\"ax-overflow-y-auto ax-max-h-[calc(100vh-303px)]\">\n @for(i of displayData() ; track i.id){\n <axm-chat-item [data]=\"i\" (click)=\"markChatAsRead(i.id)\"></axm-chat-item>\n }\n </div>\n </div>\n <div>\n <axm-chat-item-footer></axm-chat-item-footer>\n </div>\n </div>\n </div>\n <div class=\"ax-h-full md:ax-w-3/5 xl:ax-w-3/4 ax-relative\">\n <axm-chat-preview-header (pressBack)=\"goBack()\"></axm-chat-preview-header>\n <!-- <axm-chat-preview></axm-chat-preview> -->\n <router-outlet></router-outlet>\n \n </div>\n</div>\n" }]
2335
+ args: [{ selector: 'axm-comment-list-view', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-mt-2\">\n <ax-comment-container>\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 [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (checkImageExists(comment.id!) && comment.author && extractInitials(comment.author) !== '?') {\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(comment.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(comment.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\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)\">\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)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (reply && checkImageExists(reply.id!) && reply.author && extractInitials(reply.author) !== '?') {\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(reply.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(reply.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | 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 text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\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)=\"deleteReply(comment, reply)\">\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(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></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 <ax-form>\n <ax-form-field>\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-bg-on-surface 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\n class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\"\n >\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() ? extractInitials(activeReplyComment()?.author) : '' }}\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-toolbar>\n <ax-content>\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-content>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown()\"\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(true)\" 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-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>\n", styles: ["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}\n"] }]
878
2336
  }] });
879
2337
 
880
2338
  const routes = [
881
2339
  {
882
- path: '',
2340
+ path: 'platform',
883
2341
  component: AXPRootLayoutComponent,
884
2342
  children: [
885
2343
  {
886
2344
  path: 'chat',
887
- //loadComponent: () => import('./pages/chat/chat.component').then((i) => i.AXMChatComponent),
888
2345
  component: AXMChatComponent,
889
2346
  children: [
890
2347
  {
891
2348
  path: ':id',
892
- // loadComponent: () =>
893
- // import('./components/chat-preview/chat-preview.component').then((i) => i.AXMChatPreviewComponent),
894
2349
  component: AXMChatPreviewComponent,
895
2350
  },
896
2351
  ],
897
2352
  },
2353
+ {
2354
+ path: 'comments',
2355
+ component: AXMCommentListViewComponent,
2356
+ },
898
2357
  ],
899
2358
  },
900
2359
  ];
901
- class AXMChatModule {
902
- static forRoot(config) {
903
- return {
904
- ngModule: AXMChatModule,
905
- providers: [config.provider],
906
- };
907
- }
908
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
909
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: AXMChatModule, declarations: [AXMChatItemComponent,
2360
+ class AXMConversationModule {
2361
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2362
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, declarations: [
2363
+ // Chat Components
910
2364
  AXMChatComponent,
911
- AXMChatItemHeaderComponent,
2365
+ AXMChatItemComponent,
912
2366
  AXMChatPreviewComponent,
913
- AXMChatItemFooterComponent,
914
- AXMChatPreviewHeaderComponent], imports: [AXImageModule,
2367
+ // Comment Components
2368
+ AXMCommentListViewComponent], imports: [
2369
+ // Common Modules
2370
+ CommonModule,
2371
+ FormsModule, i2$1.RouterModule,
2372
+ // Acorex Core Modules
2373
+ AXFormatModule,
2374
+ AXConversationModule,
2375
+ AXCommentModule,
2376
+ AXTranslationModule,
2377
+ // Acorex Component Modules
2378
+ AXResizableDirective,
2379
+ AXImageModule,
915
2380
  AXAvatarModule,
916
2381
  AXBadgeModule,
917
2382
  AXDecoratorModule,
918
2383
  AXTextBoxModule,
2384
+ AXSearchBoxModule,
919
2385
  AXTabsModule,
920
- CommonModule,
921
- FormsModule,
922
- AXConversationModule,
923
2386
  AXButtonModule,
924
- AXLoadingModule, i1$3.RouterModule], exports: [AXMChatItemComponent,
925
- AXMChatComponent,
926
- AXMChatItemHeaderComponent,
2387
+ AXLoadingModule,
2388
+ AXWysiwygModule,
2389
+ AXLabelModule,
2390
+ AXSelectBoxModule,
2391
+ AXFormModule,
2392
+ AXDropdownButtonModule,
2393
+ AXDropdownModule,
2394
+ AXToolBarModule,
2395
+ AXSkeletonModule,
2396
+ AXPPageLayoutComponent,
2397
+ AXPThemeLayoutBlockComponent,
2398
+ AXPThemeLayoutStartSideComponent,
2399
+ AXPThemeLayoutHeaderComponent,
2400
+ AXPThemeLayoutToolbarComponent], exports: [
2401
+ // Chat Components
2402
+ AXMChatItemComponent,
927
2403
  AXMChatPreviewComponent,
928
- AXMChatItemFooterComponent,
929
- AXMChatPreviewHeaderComponent,
2404
+ // Comment Components
2405
+ AXMCommentListViewComponent,
2406
+ // Modules
930
2407
  RouterModule] }); }
931
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatModule, providers: [
2408
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, providers: [
2409
+ // Services
2410
+ {
2411
+ provide: AXMMessageService,
2412
+ useClass: AXMMessageServiceImpl,
2413
+ },
2414
+ {
2415
+ provide: AXMRoomService,
2416
+ useClass: AXMRoomServiceImpl,
2417
+ },
2418
+ // Chat and Comment Services
932
2419
  {
933
2420
  provide: AXMChatService,
934
2421
  useClass: AXMChatServiceImpl,
935
2422
  },
2423
+ {
2424
+ provide: AXMCommentService,
2425
+ useClass: AXMCommentServiceImpl,
2426
+ },
2427
+ // Entity Provider
2428
+ {
2429
+ provide: AXP_ENTITY_DEFINITION_LOADER,
2430
+ useClass: AXMConversationModuleEntityProvider,
2431
+ multi: true,
2432
+ },
2433
+ // Menu Provider
2434
+ // {
2435
+ // provide: AXP_MENU_PROVIDER,
2436
+ // useClass: AXMConversationMenuProvider,
2437
+ // multi: true,
2438
+ // },
2439
+ // Conversation Module
936
2440
  importProvidersFrom(AXConversationModule.forRoot()),
937
- ], imports: [AXImageModule,
2441
+ ], imports: [
2442
+ // Common Modules
2443
+ CommonModule,
2444
+ FormsModule,
2445
+ RouterModule.forChild(routes),
2446
+ // Acorex Core Modules
2447
+ AXFormatModule,
2448
+ AXConversationModule,
2449
+ AXCommentModule,
2450
+ AXTranslationModule,
2451
+ AXImageModule,
938
2452
  AXAvatarModule,
939
2453
  AXBadgeModule,
940
2454
  AXDecoratorModule,
941
2455
  AXTextBoxModule,
2456
+ AXSearchBoxModule,
942
2457
  AXTabsModule,
943
- CommonModule,
944
- FormsModule,
945
- AXConversationModule,
946
2458
  AXButtonModule,
947
2459
  AXLoadingModule,
948
- RouterModule.forChild(routes), RouterModule] }); }
2460
+ AXWysiwygModule,
2461
+ AXLabelModule,
2462
+ AXSelectBoxModule,
2463
+ AXFormModule,
2464
+ AXDropdownButtonModule,
2465
+ AXDropdownModule,
2466
+ AXToolBarModule,
2467
+ AXSkeletonModule,
2468
+ AXPPageLayoutComponent,
2469
+ AXPThemeLayoutBlockComponent,
2470
+ AXPThemeLayoutStartSideComponent,
2471
+ AXPThemeLayoutHeaderComponent,
2472
+ AXPThemeLayoutToolbarComponent,
2473
+ // Modules
2474
+ RouterModule] }); }
949
2475
  }
950
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMChatModule, decorators: [{
2476
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, decorators: [{
951
2477
  type: NgModule,
952
2478
  args: [{
953
2479
  declarations: [
954
- AXMChatItemComponent,
2480
+ // Chat Components
955
2481
  AXMChatComponent,
956
- AXMChatItemHeaderComponent,
2482
+ AXMChatItemComponent,
957
2483
  AXMChatPreviewComponent,
958
- AXMChatItemFooterComponent,
959
- AXMChatPreviewHeaderComponent,
2484
+ // Comment Components
2485
+ AXMCommentListViewComponent,
2486
+ // Layout Components
960
2487
  ],
961
2488
  imports: [
2489
+ // Common Modules
2490
+ CommonModule,
2491
+ FormsModule,
2492
+ RouterModule.forChild(routes),
2493
+ // Acorex Core Modules
2494
+ AXFormatModule,
2495
+ AXConversationModule,
2496
+ AXCommentModule,
2497
+ AXTranslationModule,
2498
+ // Acorex Component Modules
2499
+ AXResizableDirective,
962
2500
  AXImageModule,
963
2501
  AXAvatarModule,
964
2502
  AXBadgeModule,
965
2503
  AXDecoratorModule,
966
2504
  AXTextBoxModule,
2505
+ AXSearchBoxModule,
967
2506
  AXTabsModule,
968
- CommonModule,
969
- FormsModule,
970
- AXConversationModule,
971
2507
  AXButtonModule,
972
2508
  AXLoadingModule,
973
- RouterModule.forChild(routes),
2509
+ AXWysiwygModule,
2510
+ AXLabelModule,
2511
+ AXSelectBoxModule,
2512
+ AXFormModule,
2513
+ AXDropdownButtonModule,
2514
+ AXDropdownModule,
2515
+ AXToolBarModule,
2516
+ AXSkeletonModule,
2517
+ AXPPageLayoutComponent,
2518
+ AXPThemeLayoutBlockComponent,
2519
+ AXPThemeLayoutStartSideComponent,
2520
+ AXPThemeLayoutHeaderComponent,
2521
+ AXPThemeLayoutToolbarComponent,
974
2522
  ],
975
2523
  exports: [
2524
+ // Chat Components
976
2525
  AXMChatItemComponent,
977
- AXMChatComponent,
978
- AXMChatItemHeaderComponent,
979
2526
  AXMChatPreviewComponent,
980
- AXMChatItemFooterComponent,
981
- AXMChatPreviewHeaderComponent,
2527
+ // Comment Components
2528
+ AXMCommentListViewComponent,
2529
+ // Modules
982
2530
  RouterModule,
983
2531
  ],
984
2532
  providers: [
2533
+ // Services
2534
+ {
2535
+ provide: AXMMessageService,
2536
+ useClass: AXMMessageServiceImpl,
2537
+ },
2538
+ {
2539
+ provide: AXMRoomService,
2540
+ useClass: AXMRoomServiceImpl,
2541
+ },
2542
+ // Chat and Comment Services
985
2543
  {
986
2544
  provide: AXMChatService,
987
2545
  useClass: AXMChatServiceImpl,
988
2546
  },
2547
+ {
2548
+ provide: AXMCommentService,
2549
+ useClass: AXMCommentServiceImpl,
2550
+ },
2551
+ // Entity Provider
2552
+ {
2553
+ provide: AXP_ENTITY_DEFINITION_LOADER,
2554
+ useClass: AXMConversationModuleEntityProvider,
2555
+ multi: true,
2556
+ },
2557
+ // Menu Provider
2558
+ // {
2559
+ // provide: AXP_MENU_PROVIDER,
2560
+ // useClass: AXMConversationMenuProvider,
2561
+ // multi: true,
2562
+ // },
2563
+ // Conversation Module
989
2564
  importProvidersFrom(AXConversationModule.forRoot()),
990
2565
  ],
991
2566
  }]
992
2567
  }] });
993
2568
 
994
- class AXMConversationModule {
995
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
996
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, imports: [AXMCommentModule, AXMChatModule], exports: [AXMCommentModule, AXMChatModule] }); }
997
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, imports: [AXMCommentModule, AXMChatModule, AXMCommentModule, AXMChatModule] }); }
998
- }
999
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXMConversationModule, decorators: [{
1000
- type: NgModule,
1001
- args: [{
1002
- imports: [AXMCommentModule, AXMChatModule],
1003
- exports: [AXMCommentModule, AXMChatModule],
1004
- }]
1005
- }] });
1006
-
1007
2569
  /**
1008
2570
  * Generated bundle index. Do not edit.
1009
2571
  */
1010
2572
 
1011
- export { AXMChatComponent, AXMChatItemComponent, AXMChatItemFooterComponent, AXMChatItemHeaderComponent, AXMChatModule, AXMChatPreviewComponent, AXMChatPreviewHeaderComponent, AXMChatService, AXMChatServiceImpl, AXMCommentListViewComponent, AXMCommentLookupPopup, AXMCommentModule, AXMCommentService, AXMCommentServiceImpl, AXMConversationModule, AXMConverstionModuleConst };
2573
+ export { AXMChatComponent, AXMChatItemComponent, AXMChatPreviewComponent, AXMChatService, AXMChatServiceImpl, AXMCommentListViewComponent, AXMCommentLookupPopup, AXMCommentService, AXMCommentServiceImpl, AXMConversationModule, AXMMessageService, AXMMessageServiceImpl, AXMRoomService, AXMRoomServiceImpl, RootConfig, messageFactory, roomFactory };
1012
2574
  //# sourceMappingURL=acorex-modules-conversation.mjs.map