@acorex/modules 20.0.6 → 20.0.8

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 (190) hide show
  1. package/common/index.d.ts +22 -2
  2. package/contact-management/README.md +568 -0
  3. package/contact-management/index.d.ts +263 -0
  4. package/conversation/index.d.ts +222 -189
  5. package/document-management/index.d.ts +5 -1
  6. package/fesm2022/{acorex-modules-application-management-module-designer.component-oGgCaUDu.mjs → acorex-modules-application-management-module-designer.component-BErRgx0w.mjs} +7 -7
  7. package/fesm2022/{acorex-modules-application-management-module-designer.component-oGgCaUDu.mjs.map → acorex-modules-application-management-module-designer.component-BErRgx0w.mjs.map} +1 -1
  8. package/fesm2022/acorex-modules-application-management.mjs +41 -42
  9. package/fesm2022/acorex-modules-application-management.mjs.map +1 -1
  10. package/fesm2022/{acorex-modules-auth-acorex-modules-auth-Cj4td3eM.mjs → acorex-modules-auth-acorex-modules-auth-X866H4oy.mjs} +44 -44
  11. package/fesm2022/acorex-modules-auth-acorex-modules-auth-X866H4oy.mjs.map +1 -0
  12. package/fesm2022/{acorex-modules-auth-app-chooser.component-C60idNYx.mjs → acorex-modules-auth-app-chooser.component-DqKTfLA2.mjs} +5 -5
  13. package/fesm2022/{acorex-modules-auth-app-chooser.component-C60idNYx.mjs.map → acorex-modules-auth-app-chooser.component-DqKTfLA2.mjs.map} +1 -1
  14. package/fesm2022/{acorex-modules-auth-blank.layout-C5d42pO1.mjs → acorex-modules-auth-blank.layout-D2qSgxTy.mjs} +4 -4
  15. package/fesm2022/{acorex-modules-auth-blank.layout-C5d42pO1.mjs.map → acorex-modules-auth-blank.layout-D2qSgxTy.mjs.map} +1 -1
  16. package/fesm2022/{acorex-modules-auth-login.module-siRdi16m.mjs → acorex-modules-auth-login.module-CWOYzn0L.mjs} +8 -8
  17. package/fesm2022/{acorex-modules-auth-login.module-siRdi16m.mjs.map → acorex-modules-auth-login.module-CWOYzn0L.mjs.map} +1 -1
  18. package/fesm2022/{acorex-modules-auth-master.layout-BJCAe5Ai.mjs → acorex-modules-auth-master.layout-384T0lIg.mjs} +5 -5
  19. package/fesm2022/{acorex-modules-auth-master.layout-BJCAe5Ai.mjs.map → acorex-modules-auth-master.layout-384T0lIg.mjs.map} +1 -1
  20. package/fesm2022/{acorex-modules-auth-password.component-VhDUmkMc.mjs → acorex-modules-auth-password.component-BW_ooTQG.mjs} +5 -5
  21. package/fesm2022/{acorex-modules-auth-password.component-VhDUmkMc.mjs.map → acorex-modules-auth-password.component-BW_ooTQG.mjs.map} +1 -1
  22. package/fesm2022/{acorex-modules-auth-password.component-6-EEsfc2.mjs → acorex-modules-auth-password.component-JG95JpLz.mjs} +5 -5
  23. package/fesm2022/{acorex-modules-auth-password.component-6-EEsfc2.mjs.map → acorex-modules-auth-password.component-JG95JpLz.mjs.map} +1 -1
  24. package/fesm2022/{acorex-modules-auth-routes-BwBn4o81.mjs → acorex-modules-auth-routes-DqKtHu-i.mjs} +2 -2
  25. package/fesm2022/{acorex-modules-auth-routes-BwBn4o81.mjs.map → acorex-modules-auth-routes-DqKtHu-i.mjs.map} +1 -1
  26. package/fesm2022/{acorex-modules-auth-tenant-chooser.component-fmuXNByb.mjs → acorex-modules-auth-tenant-chooser.component-C0jxaqqg.mjs} +4 -4
  27. package/fesm2022/{acorex-modules-auth-tenant-chooser.component-fmuXNByb.mjs.map → acorex-modules-auth-tenant-chooser.component-C0jxaqqg.mjs.map} +1 -1
  28. package/fesm2022/{acorex-modules-auth-two-factor-code.component-JYtAQ3qF.mjs → acorex-modules-auth-two-factor-code.component-D7KOomHD.mjs} +4 -4
  29. package/fesm2022/{acorex-modules-auth-two-factor-code.component-JYtAQ3qF.mjs.map → acorex-modules-auth-two-factor-code.component-D7KOomHD.mjs.map} +1 -1
  30. package/fesm2022/{acorex-modules-auth-two-factor.module-D7NgZHmC.mjs → acorex-modules-auth-two-factor.module-C88pXqao.mjs} +7 -7
  31. package/fesm2022/{acorex-modules-auth-two-factor.module-D7NgZHmC.mjs.map → acorex-modules-auth-two-factor.module-C88pXqao.mjs.map} +1 -1
  32. package/fesm2022/{acorex-modules-auth-user-sessions.component-DhlBCxly.mjs → acorex-modules-auth-user-sessions.component-loloVwv0.mjs} +5 -5
  33. package/fesm2022/{acorex-modules-auth-user-sessions.component-DhlBCxly.mjs.map → acorex-modules-auth-user-sessions.component-loloVwv0.mjs.map} +1 -1
  34. package/fesm2022/acorex-modules-auth.mjs +1 -1
  35. package/fesm2022/{acorex-modules-common-search-popup.component-B5JU9uH9.mjs → acorex-modules-common-search-popup.component-CCKiQx0U.mjs} +8 -8
  36. package/fesm2022/{acorex-modules-common-search-popup.component-B5JU9uH9.mjs.map → acorex-modules-common-search-popup.component-CCKiQx0U.mjs.map} +1 -1
  37. package/fesm2022/{acorex-modules-common-timeline-version-history-popup.component-DzvUTy5z.mjs → acorex-modules-common-timeline-version-history-popup.component-DJOcjquG.mjs} +8 -8
  38. package/fesm2022/{acorex-modules-common-timeline-version-history-popup.component-DzvUTy5z.mjs.map → acorex-modules-common-timeline-version-history-popup.component-DJOcjquG.mjs.map} +1 -1
  39. package/fesm2022/acorex-modules-common.mjs +215 -86
  40. package/fesm2022/acorex-modules-common.mjs.map +1 -1
  41. package/fesm2022/acorex-modules-contact-management-address-type.entity-QMEODkhx.mjs +489 -0
  42. package/fesm2022/acorex-modules-contact-management-address-type.entity-QMEODkhx.mjs.map +1 -0
  43. package/fesm2022/acorex-modules-contact-management-contact-address.entity-0f4Vaw_k.mjs +827 -0
  44. package/fesm2022/acorex-modules-contact-management-contact-address.entity-0f4Vaw_k.mjs.map +1 -0
  45. package/fesm2022/acorex-modules-contact-management-contact-method.entity-ChEz1jW0.mjs +509 -0
  46. package/fesm2022/acorex-modules-contact-management-contact-method.entity-ChEz1jW0.mjs.map +1 -0
  47. package/fesm2022/acorex-modules-contact-management-contact-relationship.entity-DLfiEEfk.mjs +469 -0
  48. package/fesm2022/acorex-modules-contact-management-contact-relationship.entity-DLfiEEfk.mjs.map +1 -0
  49. package/fesm2022/acorex-modules-contact-management-contact-source.entity-hopVxiIT.mjs +489 -0
  50. package/fesm2022/acorex-modules-contact-management-contact-source.entity-hopVxiIT.mjs.map +1 -0
  51. package/fesm2022/acorex-modules-contact-management.mjs +2332 -0
  52. package/fesm2022/acorex-modules-contact-management.mjs.map +1 -0
  53. package/fesm2022/acorex-modules-content-management.mjs +119 -55
  54. package/fesm2022/acorex-modules-content-management.mjs.map +1 -1
  55. package/fesm2022/acorex-modules-conversation.mjs +1174 -1216
  56. package/fesm2022/acorex-modules-conversation.mjs.map +1 -1
  57. package/fesm2022/acorex-modules-dashboard-management.mjs +76 -76
  58. package/fesm2022/acorex-modules-dashboard-management.mjs.map +1 -1
  59. package/fesm2022/{acorex-modules-document-management-acorex-modules-document-management-ClH75xx1.mjs → acorex-modules-document-management-acorex-modules-document-management-C4cvhxSV.mjs} +310 -235
  60. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-C4cvhxSV.mjs.map +1 -0
  61. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-B_tjfzIn.mjs → acorex-modules-document-management-attachment-widget.component-B5txg8z0.mjs} +5 -5
  62. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-B_tjfzIn.mjs.map → acorex-modules-document-management-attachment-widget.component-B5txg8z0.mjs.map} +1 -1
  63. package/fesm2022/{acorex-modules-document-management-create-folder-dialog.component-DNdRcCAs.mjs → acorex-modules-document-management-create-folder-dialog.component-LfnTz49a.mjs} +5 -5
  64. package/fesm2022/{acorex-modules-document-management-create-folder-dialog.component-DNdRcCAs.mjs.map → acorex-modules-document-management-create-folder-dialog.component-LfnTz49a.mjs.map} +1 -1
  65. package/fesm2022/{acorex-modules-document-management-details-view.component-JsK1UgZh.mjs → acorex-modules-document-management-details-view.component-Co5F8eWp.mjs} +5 -5
  66. package/fesm2022/{acorex-modules-document-management-details-view.component-JsK1UgZh.mjs.map → acorex-modules-document-management-details-view.component-Co5F8eWp.mjs.map} +1 -1
  67. package/fesm2022/{acorex-modules-document-management-drive-choose.component-DJT9oLek.mjs → acorex-modules-document-management-drive-choose.component-DFRdS7s1.mjs} +5 -5
  68. package/fesm2022/{acorex-modules-document-management-drive-choose.component-DJT9oLek.mjs.map → acorex-modules-document-management-drive-choose.component-DFRdS7s1.mjs.map} +1 -1
  69. package/fesm2022/{acorex-modules-document-management-drive.component-DYxpvUWX.mjs → acorex-modules-document-management-drive.component-jGs09Jrd.mjs} +8 -8
  70. package/fesm2022/{acorex-modules-document-management-drive.component-DYxpvUWX.mjs.map → acorex-modules-document-management-drive.component-jGs09Jrd.mjs.map} +1 -1
  71. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-B324V2hL.mjs → acorex-modules-document-management-large-icons-view.component-DD9_xgL4.mjs} +5 -5
  72. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-B324V2hL.mjs.map → acorex-modules-document-management-large-icons-view.component-DD9_xgL4.mjs.map} +1 -1
  73. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-V5gHyu9X.mjs → acorex-modules-document-management-large-tiles-view.component-CNBUzaUz.mjs} +5 -5
  74. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-V5gHyu9X.mjs.map → acorex-modules-document-management-large-tiles-view.component-CNBUzaUz.mjs.map} +1 -1
  75. package/fesm2022/{acorex-modules-document-management-link-dialog.component-B-BzU_4Y.mjs → acorex-modules-document-management-link-dialog.component-CXzR7mUW.mjs} +4 -4
  76. package/fesm2022/{acorex-modules-document-management-link-dialog.component-B-BzU_4Y.mjs.map → acorex-modules-document-management-link-dialog.component-CXzR7mUW.mjs.map} +1 -1
  77. package/fesm2022/{acorex-modules-document-management-list-view.component-Bm2EH2aT.mjs → acorex-modules-document-management-list-view.component-D4DbyB6W.mjs} +5 -5
  78. package/fesm2022/{acorex-modules-document-management-list-view.component-Bm2EH2aT.mjs.map → acorex-modules-document-management-list-view.component-D4DbyB6W.mjs.map} +1 -1
  79. package/fesm2022/acorex-modules-document-management-meta-choose-popup.component-CmMvrfKW.mjs +315 -0
  80. package/fesm2022/acorex-modules-document-management-meta-choose-popup.component-CmMvrfKW.mjs.map +1 -0
  81. package/fesm2022/{acorex-modules-document-management-meta-selector-widget-column.component-BDhqsc2j.mjs → acorex-modules-document-management-meta-selector-widget-column.component-DHQjyq8s.mjs} +4 -4
  82. package/fesm2022/{acorex-modules-document-management-meta-selector-widget-column.component-BDhqsc2j.mjs.map → acorex-modules-document-management-meta-selector-widget-column.component-DHQjyq8s.mjs.map} +1 -1
  83. package/fesm2022/{acorex-modules-document-management-meta-selector-widget-designer.component-C4GC0GbF.mjs → acorex-modules-document-management-meta-selector-widget-designer.component-Cc6n71wn.mjs} +4 -4
  84. package/fesm2022/{acorex-modules-document-management-meta-selector-widget-designer.component-C4GC0GbF.mjs.map → acorex-modules-document-management-meta-selector-widget-designer.component-Cc6n71wn.mjs.map} +1 -1
  85. package/fesm2022/acorex-modules-document-management-meta-selector-widget-view.component-D88h9eR6.mjs +31 -0
  86. package/fesm2022/acorex-modules-document-management-meta-selector-widget-view.component-D88h9eR6.mjs.map +1 -0
  87. package/fesm2022/{acorex-modules-document-management-permission-definition.provider-DGqVDVLJ.mjs → acorex-modules-document-management-permission-definition.provider-DoOyhnwQ.mjs} +2 -2
  88. package/fesm2022/{acorex-modules-document-management-permission-definition.provider-DGqVDVLJ.mjs.map → acorex-modules-document-management-permission-definition.provider-DoOyhnwQ.mjs.map} +1 -1
  89. package/fesm2022/{acorex-modules-document-management-rename-node-dialog.component-LgpvDguk.mjs → acorex-modules-document-management-rename-node-dialog.component-JDZWsFXL.mjs} +5 -5
  90. package/fesm2022/{acorex-modules-document-management-rename-node-dialog.component-LgpvDguk.mjs.map → acorex-modules-document-management-rename-node-dialog.component-JDZWsFXL.mjs.map} +1 -1
  91. package/fesm2022/{acorex-modules-document-management-share-dialog.component-DzuYhAD4.mjs → acorex-modules-document-management-share-dialog.component-DTzXwqHw.mjs} +4 -4
  92. package/fesm2022/{acorex-modules-document-management-share-dialog.component-DzuYhAD4.mjs.map → acorex-modules-document-management-share-dialog.component-DTzXwqHw.mjs.map} +1 -1
  93. package/fesm2022/{acorex-modules-document-management-share-email-dialog.component-DNN_3JF2.mjs → acorex-modules-document-management-share-email-dialog.component-BaNzKKG4.mjs} +4 -4
  94. package/fesm2022/{acorex-modules-document-management-share-email-dialog.component-DNN_3JF2.mjs.map → acorex-modules-document-management-share-email-dialog.component-BaNzKKG4.mjs.map} +1 -1
  95. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-BwFoe1vd.mjs → acorex-modules-document-management-small-icons-view.component-BNTmgtYr.mjs} +5 -5
  96. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-BwFoe1vd.mjs.map → acorex-modules-document-management-small-icons-view.component-BNTmgtYr.mjs.map} +1 -1
  97. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-B5gp0wus.mjs → acorex-modules-document-management-small-tiles-view.component-CbCBAKyR.mjs} +5 -5
  98. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-B5gp0wus.mjs.map → acorex-modules-document-management-small-tiles-view.component-CbCBAKyR.mjs.map} +1 -1
  99. package/fesm2022/acorex-modules-document-management.mjs +1 -1
  100. package/fesm2022/{acorex-modules-form-template-management-acorex-modules-form-template-management-B3o427If.mjs → acorex-modules-form-template-management-acorex-modules-form-template-management-BsAsK1EG.mjs} +58 -57
  101. package/fesm2022/{acorex-modules-form-template-management-acorex-modules-form-template-management-B3o427If.mjs.map → acorex-modules-form-template-management-acorex-modules-form-template-management-BsAsK1EG.mjs.map} +1 -1
  102. package/fesm2022/{acorex-modules-form-template-management-category.entity-B7dGrmoa.mjs → acorex-modules-form-template-management-category.entity-B3iAtREq.mjs} +2 -2
  103. package/fesm2022/{acorex-modules-form-template-management-category.entity-B7dGrmoa.mjs.map → acorex-modules-form-template-management-category.entity-B3iAtREq.mjs.map} +1 -1
  104. package/fesm2022/{acorex-modules-form-template-management-designer.page-DaTGU3w_.mjs → acorex-modules-form-template-management-designer.page-B-TIUKl7.mjs} +4 -4
  105. package/fesm2022/{acorex-modules-form-template-management-designer.page-DaTGU3w_.mjs.map → acorex-modules-form-template-management-designer.page-B-TIUKl7.mjs.map} +1 -1
  106. package/fesm2022/{acorex-modules-form-template-management-permission-definition.provider-CjjlgO0w.mjs → acorex-modules-form-template-management-permission-definition.provider-CTG95UDp.mjs} +2 -2
  107. package/fesm2022/{acorex-modules-form-template-management-permission-definition.provider-CjjlgO0w.mjs.map → acorex-modules-form-template-management-permission-definition.provider-CTG95UDp.mjs.map} +1 -1
  108. package/fesm2022/{acorex-modules-form-template-management-settings.provider-CiSjuHGI.mjs → acorex-modules-form-template-management-settings.provider-BmFufTUd.mjs} +2 -2
  109. package/fesm2022/{acorex-modules-form-template-management-settings.provider-CiSjuHGI.mjs.map → acorex-modules-form-template-management-settings.provider-BmFufTUd.mjs.map} +1 -1
  110. package/fesm2022/{acorex-modules-form-template-management-template-picker.component-CQZ37yC2.mjs → acorex-modules-form-template-management-template-picker.component-CNwhdnGC.mjs} +4 -4
  111. package/fesm2022/{acorex-modules-form-template-management-template-picker.component-CQZ37yC2.mjs.map → acorex-modules-form-template-management-template-picker.component-CNwhdnGC.mjs.map} +1 -1
  112. package/fesm2022/{acorex-modules-form-template-management-template-widget-edit.component-DUeV0yO8.mjs → acorex-modules-form-template-management-template-widget-edit.component-BNIx16WW.mjs} +5 -5
  113. package/fesm2022/{acorex-modules-form-template-management-template-widget-edit.component-DUeV0yO8.mjs.map → acorex-modules-form-template-management-template-widget-edit.component-BNIx16WW.mjs.map} +1 -1
  114. package/fesm2022/{acorex-modules-form-template-management-template.entity-Cps77n3G.mjs → acorex-modules-form-template-management-template.entity-BGsixiEI.mjs} +2 -2
  115. package/fesm2022/{acorex-modules-form-template-management-template.entity-Cps77n3G.mjs.map → acorex-modules-form-template-management-template.entity-BGsixiEI.mjs.map} +1 -1
  116. package/fesm2022/{acorex-modules-form-template-management-viewer-popup.component-K2ZznaDw.mjs → acorex-modules-form-template-management-viewer-popup.component-CzjuUz4o.mjs} +9 -6
  117. package/fesm2022/acorex-modules-form-template-management-viewer-popup.component-CzjuUz4o.mjs.map +1 -0
  118. package/fesm2022/acorex-modules-form-template-management.mjs +1 -1
  119. package/fesm2022/{acorex-modules-issue-management-acorex-modules-issue-management-Blyh8QVB.mjs → acorex-modules-issue-management-acorex-modules-issue-management-C2JoHgaa.mjs} +24 -24
  120. package/fesm2022/{acorex-modules-issue-management-acorex-modules-issue-management-Blyh8QVB.mjs.map → acorex-modules-issue-management-acorex-modules-issue-management-C2JoHgaa.mjs.map} +1 -1
  121. package/fesm2022/{acorex-modules-issue-management-capture-screen.component-BzUN6Cty.mjs → acorex-modules-issue-management-capture-screen.component-DTtUkL78.mjs} +5 -5
  122. package/fesm2022/{acorex-modules-issue-management-capture-screen.component-BzUN6Cty.mjs.map → acorex-modules-issue-management-capture-screen.component-DTtUkL78.mjs.map} +1 -1
  123. package/fesm2022/acorex-modules-issue-management.mjs +1 -1
  124. package/fesm2022/acorex-modules-log-management.mjs +10 -10
  125. package/fesm2022/acorex-modules-log-management.mjs.map +1 -1
  126. package/fesm2022/acorex-modules-notification-management.mjs +35 -35
  127. package/fesm2022/acorex-modules-notification-management.mjs.map +1 -1
  128. package/fesm2022/{acorex-modules-organization-management-add-item.component-COUAmxM8.mjs → acorex-modules-organization-management-add-item.component-D99TugmE.mjs} +4 -4
  129. package/fesm2022/{acorex-modules-organization-management-add-item.component-COUAmxM8.mjs.map → acorex-modules-organization-management-add-item.component-D99TugmE.mjs.map} +1 -1
  130. package/fesm2022/{acorex-modules-organization-management-org-chart-configuration.page-DjGUUbel.mjs → acorex-modules-organization-management-org-chart-configuration.page-DTNqHbxt.mjs} +5 -5
  131. package/fesm2022/{acorex-modules-organization-management-org-chart-configuration.page-DjGUUbel.mjs.map → acorex-modules-organization-management-org-chart-configuration.page-DTNqHbxt.mjs.map} +1 -1
  132. package/fesm2022/{acorex-modules-organization-management-org-chart-configuration.service-DeoDX_ir.mjs → acorex-modules-organization-management-org-chart-configuration.service-BSO0GrAW.mjs} +4 -4
  133. package/fesm2022/{acorex-modules-organization-management-org-chart-configuration.service-DeoDX_ir.mjs.map → acorex-modules-organization-management-org-chart-configuration.service-BSO0GrAW.mjs.map} +1 -1
  134. package/fesm2022/{acorex-modules-organization-management-org-chart.page-xlNWhN4W.mjs → acorex-modules-organization-management-org-chart.page-ioMhEPnQ.mjs} +12 -12
  135. package/fesm2022/{acorex-modules-organization-management-org-chart.page-xlNWhN4W.mjs.map → acorex-modules-organization-management-org-chart.page-ioMhEPnQ.mjs.map} +1 -1
  136. package/fesm2022/acorex-modules-organization-management.mjs +122 -122
  137. package/fesm2022/acorex-modules-organization-management.mjs.map +1 -1
  138. package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-Dfux0tkB.mjs → acorex-modules-platform-management-acorex-modules-platform-management-CFoPmixl.mjs} +933 -315
  139. package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-CFoPmixl.mjs.map +1 -0
  140. package/fesm2022/{acorex-modules-platform-management-list-version.component-CSNKxghv.mjs → acorex-modules-platform-management-list-version.component-D0-Ya-Yx.mjs} +7 -7
  141. package/fesm2022/{acorex-modules-platform-management-list-version.component-CSNKxghv.mjs.map → acorex-modules-platform-management-list-version.component-D0-Ya-Yx.mjs.map} +1 -1
  142. package/fesm2022/{acorex-modules-platform-management-settings.provider-C4RaOgkr.mjs → acorex-modules-platform-management-settings.provider--ARXqBUY.mjs} +2 -2
  143. package/fesm2022/{acorex-modules-platform-management-settings.provider-C4RaOgkr.mjs.map → acorex-modules-platform-management-settings.provider--ARXqBUY.mjs.map} +1 -1
  144. package/fesm2022/acorex-modules-platform-management.mjs +1 -1
  145. package/fesm2022/acorex-modules-project-management.mjs +43 -43
  146. package/fesm2022/acorex-modules-project-management.mjs.map +1 -1
  147. package/fesm2022/acorex-modules-report-management-report-create-root.component-LrE6TkIp.mjs +302 -0
  148. package/fesm2022/acorex-modules-report-management-report-create-root.component-LrE6TkIp.mjs.map +1 -0
  149. package/fesm2022/{acorex-modules-report-management-report-runner-root-page.component-CtEDcg_B.mjs → acorex-modules-report-management-report-runner-root-page.component-BntIoMfN.mjs} +49 -21
  150. package/fesm2022/acorex-modules-report-management-report-runner-root-page.component-BntIoMfN.mjs.map +1 -0
  151. package/fesm2022/acorex-modules-report-management-run.command-CoXvPC15.mjs +20 -0
  152. package/fesm2022/acorex-modules-report-management-run.command-CoXvPC15.mjs.map +1 -0
  153. package/fesm2022/acorex-modules-report-management.mjs +797 -29
  154. package/fesm2022/acorex-modules-report-management.mjs.map +1 -1
  155. package/fesm2022/acorex-modules-scheduler-job-management.mjs +14 -14
  156. package/fesm2022/acorex-modules-scheduler-job-management.mjs.map +1 -1
  157. package/fesm2022/acorex-modules-security-management.mjs +63 -69
  158. package/fesm2022/acorex-modules-security-management.mjs.map +1 -1
  159. package/fesm2022/{acorex-modules-settings-management-setting-page.component-B2CEFpES.mjs → acorex-modules-settings-management-setting-page.component-BKwKLUpD.mjs} +4 -4
  160. package/fesm2022/{acorex-modules-settings-management-setting-page.component-B2CEFpES.mjs.map → acorex-modules-settings-management-setting-page.component-BKwKLUpD.mjs.map} +1 -1
  161. package/fesm2022/{acorex-modules-settings-management-setting-view.component-DnD83gFz.mjs → acorex-modules-settings-management-setting-view.component-Dco7MvZr.mjs} +4 -4
  162. package/fesm2022/{acorex-modules-settings-management-setting-view.component-DnD83gFz.mjs.map → acorex-modules-settings-management-setting-view.component-Dco7MvZr.mjs.map} +1 -1
  163. package/fesm2022/acorex-modules-settings-management.mjs +6 -6
  164. package/fesm2022/acorex-modules-settings-management.mjs.map +1 -1
  165. package/fesm2022/acorex-modules-text-template-management.mjs +24 -24
  166. package/fesm2022/acorex-modules-text-template-management.mjs.map +1 -1
  167. package/fesm2022/acorex-modules-training-management.mjs +43 -44
  168. package/fesm2022/acorex-modules-training-management.mjs.map +1 -1
  169. package/fesm2022/acorex-modules-workflow-management-task-board.page-ChW_Hwf2.mjs +762 -0
  170. package/fesm2022/acorex-modules-workflow-management-task-board.page-ChW_Hwf2.mjs.map +1 -0
  171. package/fesm2022/acorex-modules-workflow-management.mjs +12 -12
  172. package/fesm2022/acorex-modules-workflow-management.mjs.map +1 -1
  173. package/package.json +9 -5
  174. package/platform-management/index.d.ts +235 -8
  175. package/report-management/index.d.ts +11 -4
  176. package/fesm2022/acorex-modules-auth-acorex-modules-auth-Cj4td3eM.mjs.map +0 -1
  177. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-ClH75xx1.mjs.map +0 -1
  178. package/fesm2022/acorex-modules-document-management-lock-dialog.component-DvZ-vrUE.mjs +0 -51
  179. package/fesm2022/acorex-modules-document-management-lock-dialog.component-DvZ-vrUE.mjs.map +0 -1
  180. package/fesm2022/acorex-modules-document-management-meta-choose-popup.component-q6mDKrFi.mjs +0 -190
  181. package/fesm2022/acorex-modules-document-management-meta-choose-popup.component-q6mDKrFi.mjs.map +0 -1
  182. package/fesm2022/acorex-modules-document-management-meta-selector-widget-view.component-rlSWRGGl.mjs +0 -32
  183. package/fesm2022/acorex-modules-document-management-meta-selector-widget-view.component-rlSWRGGl.mjs.map +0 -1
  184. package/fesm2022/acorex-modules-document-management-unlock-dialog.component-B_TMgAl1.mjs +0 -36
  185. package/fesm2022/acorex-modules-document-management-unlock-dialog.component-B_TMgAl1.mjs.map +0 -1
  186. package/fesm2022/acorex-modules-form-template-management-viewer-popup.component-K2ZznaDw.mjs.map +0 -1
  187. package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-Dfux0tkB.mjs.map +0 -1
  188. package/fesm2022/acorex-modules-report-management-report-runner-root-page.component-CtEDcg_B.mjs.map +0 -1
  189. package/fesm2022/acorex-modules-workflow-management-task-board.page-Bugxqd0W.mjs +0 -494
  190. package/fesm2022/acorex-modules-workflow-management-task-board.page-Bugxqd0W.mjs.map +0 -1
@@ -1,75 +1,76 @@
1
1
  import * as i2$4 from '@acorex/components/conversation';
2
- import { AXConversationModule } from '@acorex/components/conversation';
2
+ import { AXConversationModule, AXConversationInputComponent } from '@acorex/components/conversation';
3
3
  import { AXPSessionService, AXPAuthGuard } from '@acorex/platform/auth';
4
- import { AXMEntityCrudServiceImpl, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
4
+ import { AXMEntityCrudServiceImpl, AXPEntityService, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
5
5
  import { AXPRootLayoutComponent } from '@acorex/platform/themes/default';
6
- import * as i1$1 from '@angular/common';
7
- import { AsyncPipe, DatePipe, CommonModule } from '@angular/common';
6
+ import * as i1$2 from '@angular/common';
7
+ import { AsyncPipe, CommonModule, DatePipe } from '@angular/common';
8
8
  import * as i0 from '@angular/core';
9
- import { Injectable, inject, signal, Component, viewChild, computed, ChangeDetectionStrategy, input, output, effect, afterNextRender, importProvidersFrom, NgModule } from '@angular/core';
9
+ import { Injectable, inject, input, signal, viewChild, DestroyRef, ChangeDetectionStrategy, Component, ViewEncapsulation, computed, output, effect, afterNextRender, HostListener, importProvidersFrom, NgModule } from '@angular/core';
10
10
  import * as i2$3 from '@angular/router';
11
11
  import { ActivatedRoute, Router, NavigationEnd, RouterModule, ROUTES } from '@angular/router';
12
- import { createAllQueryView, AXPEntityCommandScope, AXPEntityQueryType } from '@acorex/platform/common';
13
- import * as i2 from '@acorex/platform/layout/builder';
14
- import { AXPWidgetsCatalog, AXPLayoutBuilderModule, AXPLayoutWidgetComponent, AXPWidgetGroupEnum } from '@acorex/platform/layout/builder';
15
- import { AXResizableDirective } from '@acorex/cdk/resizable';
16
- import * as i6 from '@acorex/components/avatar';
12
+ import { createAllQueryView, AXPEntityCommandScope, AXPEntityQueryType, AXPFilterOperatorMiddlewareService, AXP_MENU_PROVIDER } from '@acorex/platform/common';
13
+ import * as i2$1 from '@acorex/platform/layout/builder';
14
+ import { AXPWidgetsCatalog, AXPLayoutBuilderModule, AXPValueWidgetComponent, AXPWidgetGroupEnum } from '@acorex/platform/layout/builder';
15
+ import * as i2$5 from '@acorex/platform/workflow';
16
+ import { AXPWorkflowAction, AXPWorkflowModule } from '@acorex/platform/workflow';
17
+ import * as i3 from '@acorex/components/avatar';
17
18
  import { AXAvatarModule } from '@acorex/components/avatar';
18
19
  import * as i2$2 from '@acorex/components/badge';
19
20
  import { AXBadgeModule } from '@acorex/components/badge';
20
- import * as i5$1 from '@acorex/components/button';
21
+ import * as i5 from '@acorex/components/button';
21
22
  import { AXButtonModule } from '@acorex/components/button';
22
23
  import * as i4 from '@acorex/components/comment';
23
24
  import { AXCommentModule } from '@acorex/components/comment';
24
- import * as i5 from '@acorex/components/decorators';
25
+ import * as i1$1 from '@acorex/components/decorators';
25
26
  import { AXDecoratorModule } from '@acorex/components/decorators';
26
- import * as i9 from '@acorex/components/dropdown';
27
+ import { AXDialogService } from '@acorex/components/dialog';
28
+ import * as i7 from '@acorex/components/dropdown';
27
29
  import { AXDropdownModule } from '@acorex/components/dropdown';
28
- import * as i14 from '@acorex/components/dropdown-button';
30
+ import * as i12 from '@acorex/components/dropdown-button';
29
31
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
30
- import * as i11 from '@acorex/components/form';
32
+ import * as i9 from '@acorex/components/form';
31
33
  import { AXFormModule } from '@acorex/components/form';
32
- import * as i7 from '@acorex/components/image';
33
34
  import { AXImageModule } from '@acorex/components/image';
34
35
  import { AXLabelModule } from '@acorex/components/label';
35
- import * as i8 from '@acorex/components/loading';
36
+ import * as i6 from '@acorex/components/loading';
36
37
  import { AXLoadingModule } from '@acorex/components/loading';
37
38
  import { AXPopupService } from '@acorex/components/popup';
38
39
  import * as i6$1 from '@acorex/components/search-box';
39
40
  import { AXSearchBoxModule } from '@acorex/components/search-box';
40
41
  import { AXSelectBoxModule } from '@acorex/components/select-box';
41
- import * as i2$1 from '@acorex/components/skeleton';
42
+ import * as i2 from '@acorex/components/skeleton';
42
43
  import { AXSkeletonModule } from '@acorex/components/skeleton';
43
44
  import * as i7$1 from '@acorex/components/tabs';
44
45
  import { AXTabsModule } from '@acorex/components/tabs';
45
46
  import { AXTextBoxModule } from '@acorex/components/text-box';
46
- import * as i12 from '@acorex/components/toolbar';
47
+ import * as i10 from '@acorex/components/toolbar';
47
48
  import { AXToolBarModule } from '@acorex/components/toolbar';
48
49
  import * as i1 from '@acorex/components/wysiwyg';
49
- import { AXWysiwygModule, AXWysiwygContainerComponent } from '@acorex/components/wysiwyg';
50
- import * as i10 from '@acorex/core/format';
50
+ import { AXWysiwygModule } from '@acorex/components/wysiwyg';
51
+ import * as i8 from '@acorex/core/format';
51
52
  import { AXFormatModule } from '@acorex/core/format';
52
- import * as i3 from '@acorex/core/translation';
53
- import { AXTranslationModule } from '@acorex/core/translation';
53
+ import * as i4$1 from '@acorex/core/translation';
54
+ import { AXTranslationModule, AXTranslationService } from '@acorex/core/translation';
54
55
  import { AXUnsubscriber } from '@acorex/core/utils';
55
- import { AXMUsersEntityService } from '@acorex/modules/security-management';
56
56
  import { AXPUserAvatarComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutToolbarComponent } from '@acorex/platform/layout/components';
57
57
  import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageLayoutBase } from '@acorex/platform/layout/views';
58
- import * as i13 from '@angular/forms';
58
+ import { trigger, transition, style, animate } from '@angular/animations';
59
+ import * as i11 from '@angular/forms';
59
60
  import { FormsModule } from '@angular/forms';
60
61
  import { Subject, filter, startWith } from 'rxjs';
61
- import { AXDialogService } from '@acorex/components/dialog';
62
62
  import { AXToastService } from '@acorex/components/toast';
63
- import { AXPlatform } from '@acorex/core/platform';
64
63
  import { DomSanitizer } from '@angular/platform-browser';
64
+ import { AXMUsersEntityService } from '@acorex/modules/security-management';
65
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
65
66
  import { AXBasePageComponent } from '@acorex/components/page';
66
67
  import { AXDomChangeDirective } from '@acorex/cdk/dom';
67
68
  import { AXFileService } from '@acorex/core/file';
68
- import { AXValidationModule } from '@acorex/core/validation';
69
69
 
70
70
  const config = {
71
71
  i18n: 'conversation',
72
72
  module: 'Conversation',
73
+ route: 'chat',
73
74
  };
74
75
  const RootConfig = {
75
76
  config,
@@ -101,10 +102,10 @@ class AXMMessageServiceImpl extends AXMMessageService {
101
102
  constructor() {
102
103
  super(`${RootConfig.module.name}.${RootConfig.entities.message.name}`);
103
104
  }
104
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMMessageServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
105
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMMessageServiceImpl }); }
105
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMMessageServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
106
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMMessageServiceImpl }); }
106
107
  }
107
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMMessageServiceImpl, decorators: [{
108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMMessageServiceImpl, decorators: [{
108
109
  type: Injectable
109
110
  }], ctorParameters: () => [] });
110
111
 
@@ -379,10 +380,10 @@ class AXMRoomServiceImpl extends AXMRoomService {
379
380
  constructor() {
380
381
  super(`${RootConfig.module.name}.${RootConfig.entities.room.name}`);
381
382
  }
382
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMRoomServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
383
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMRoomServiceImpl }); }
383
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMRoomServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
384
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMRoomServiceImpl }); }
384
385
  }
385
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMRoomServiceImpl, decorators: [{
386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMRoomServiceImpl, decorators: [{
386
387
  type: Injectable
387
388
  }], ctorParameters: () => [] });
388
389
 
@@ -670,395 +671,331 @@ class AXMConversationModuleEntityProvider {
670
671
  return null;
671
672
  }
672
673
  }
673
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModuleEntityProvider, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
674
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModuleEntityProvider }); }
674
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModuleEntityProvider, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
675
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModuleEntityProvider }); }
675
676
  }
676
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModuleEntityProvider, decorators: [{
677
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModuleEntityProvider, decorators: [{
677
678
  type: Injectable
678
679
  }], ctorParameters: () => [{ type: i0.Injector }] });
679
680
 
680
- /**
681
- * Abstract Comment Service defining the contract for comment functionality
682
- */
681
+ class AXMMenuProvider {
682
+ constructor() {
683
+ this.sessionService = inject(AXPSessionService);
684
+ this.entityService = inject(AXPEntityService);
685
+ }
686
+ async provide(context) {
687
+ const module = RootConfig;
688
+ const scope = RootConfig.config.i18n;
689
+ context.addItems([
690
+ {
691
+ text: `t('plural-title', {scope:"${scope}"})`,
692
+ path: `${this.sessionService.application?.name}/${module.config.route}`,
693
+ priority: 2,
694
+ icon: RootConfig.entities.room.icon,
695
+ },
696
+ ]);
697
+ }
698
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMMenuProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
699
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMMenuProvider }); }
700
+ }
701
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMMenuProvider, decorators: [{
702
+ type: Injectable
703
+ }] });
704
+
705
+ //#region ---- Abstract Comment Service ----
683
706
  class AXMCommentService {
684
707
  }
708
+ //#endregion
685
709
  class AXMCommentServiceImpl {
686
710
  constructor() {
711
+ //#region ---- Dependencies ----
687
712
  this.roomService = inject(AXMRoomService);
688
713
  this.messageService = inject(AXMMessageService);
689
714
  this.sessionService = inject(AXPSessionService);
690
715
  this.usersService = inject(AXMUsersEntityService);
716
+ this.filterService = inject(AXPFilterOperatorMiddlewareService);
717
+ //#endregion
718
+ //#region ---- Observables ----
719
+ this._commentAdded$ = new Subject();
720
+ this.commentAdded$ = this._commentAdded$.asObservable();
721
+ this._commentRemoved$ = new Subject();
722
+ this.commentRemoved$ = this._commentRemoved$.asObservable();
723
+ this._commentUpdated$ = new Subject();
724
+ this.commentUpdated$ = this._commentUpdated$.asObservable();
725
+ this._threadAdded$ = new Subject();
726
+ this.threadAdded$ = this._threadAdded$.asObservable();
727
+ this._threadRemoved$ = new Subject();
728
+ this.threadRemoved$ = this._threadRemoved$.asObservable();
691
729
  }
692
- // === Helper Methods ===
730
+ //#endregion
731
+ //#region ---- Helper Methods ----
693
732
  getCurrentUser() {
694
733
  const user = this.sessionService.user;
734
+ if (!user) {
735
+ throw new Error('No authenticated user found.');
736
+ }
695
737
  return {
696
- id: user?.id || 'guest-user',
738
+ id: user.id,
697
739
  type: 'user',
698
740
  };
699
741
  }
700
742
  async getUserInfo(userId) {
701
743
  try {
702
- const user = await this.usersService.getOne(userId);
703
- return {
704
- id: user.id,
705
- userName: user.username || user.id,
706
- firstName: user.firstName || '',
707
- lastName: user.lastName || '',
708
- fullName: `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'Unknown User',
709
- avatar: '',
710
- };
744
+ await this.usersService.getOne(userId); // check if user exists
745
+ return { id: userId, type: 'user' };
711
746
  }
712
- catch (error) {
713
- console.error(`Failed to get user info for ID: ${userId}`, error);
714
- return {
715
- id: userId,
716
- userName: userId,
717
- firstName: 'Unknown',
718
- lastName: 'User',
719
- fullName: 'Unknown User',
720
- avatar: '',
721
- };
747
+ catch {
748
+ throw new Error(`User with ID ${userId} not found`);
722
749
  }
723
750
  }
724
- formatRoomId(entityId, instanceId, roomType = 'default') {
725
- return `${roomType}:${entityId}:${instanceId}`;
726
- }
727
- async formatComment(message) {
728
- const currentUserId = this.getCurrentUser().id;
729
- // Check if the current user has liked this message
730
- const isLiked = (message.reactions || []).some((reaction) => reaction.author.id === currentUserId && reaction.type === 'like');
731
- // Count total reactions
732
- const reactionsCount = (message.reactions || []).filter((r) => r.type === 'like').length;
733
- // Get the full user info for the author
734
- const userInfo = await this.getUserInfo(message.author.id);
751
+ formatComment(message, depth = 0) {
735
752
  return {
736
753
  ...message,
737
- author: {
738
- ...message.author,
739
- ...userInfo,
740
- },
741
- isFromCurrentUser: message.author.id === currentUserId,
742
- formattedDate: new Date(message.createdAt || new Date()).toLocaleString(),
743
- isLiked,
744
- reactionsCount,
754
+ depth,
745
755
  replies: [],
756
+ replyCount: 0,
746
757
  };
747
758
  }
748
- async buildCommentHierarchy(comments) {
749
- // Create a map for quick lookup of comments by ID
750
- const commentMap = new Map();
751
- const rootComments = [];
752
- // First pass: add all comments to the map
753
- for (const comment of comments) {
754
- commentMap.set(comment.id, comment);
755
- // Initialize the replies array if not already present
756
- if (!comment.replies) {
757
- comment.replies = [];
758
- }
759
- // If this is a root comment (no replyId), add it to the root comments array
760
- if (!comment.replyId) {
761
- rootComments.push(comment);
762
- }
763
- }
764
- // Second pass: build the hierarchy by adding replies to their parent comments
765
- for (const comment of comments) {
766
- if (comment.replyId) {
767
- const parentComment = commentMap.get(comment.replyId);
768
- if (parentComment && parentComment.replies) {
769
- parentComment.replies.push(comment);
770
- }
771
- else {
772
- // If parent not found (might be deleted), treat as a root comment
773
- rootComments.push(comment);
774
- }
759
+ buildCommentTree(comments) {
760
+ const commentMap = new Map(comments.map((c) => [c.id, c]));
761
+ const tree = [];
762
+ comments.forEach((comment) => {
763
+ if (comment.replyId && commentMap.has(comment.replyId)) {
764
+ const parent = commentMap.get(comment.replyId);
765
+ parent.replies.push(comment);
766
+ comment.depth = parent.depth + 1;
775
767
  }
776
- }
777
- // Sort the root comments and their replies by creation date (newest first)
778
- const sortByDate = (a, b) => {
779
- const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
780
- const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
781
- return dateB - dateA;
782
- };
783
- rootComments.sort(sortByDate);
784
- // Sort the replies within each comment
785
- for (const comment of rootComments) {
786
- if (comment.replies && comment.replies.length > 0) {
787
- comment.replies.sort(sortByDate);
768
+ else {
769
+ tree.push(comment);
788
770
  }
789
- }
790
- return rootComments;
791
- }
792
- // === Room Operations ===
793
- async getRoomList(skip = 0, take = 100) {
794
- return this.roomService.query({ skip, take });
795
- }
796
- async getRoomDetails(roomId) {
797
- return this.roomService.getOne(roomId);
798
- }
799
- async createRoom(roomData) {
800
- return this.roomService.insertOne(roomData);
771
+ });
772
+ commentMap.forEach((comment) => {
773
+ comment.replyCount = comment.replies.length;
774
+ });
775
+ return tree;
801
776
  }
802
- async ensureRoomExists(entityId, instanceId, roomType = 'default') {
803
- const roomId = this.formatRoomId(entityId, instanceId, roomType);
804
- try {
805
- // Check if room exists
806
- await this.getRoomDetails(roomId);
807
- return roomId;
808
- }
809
- catch (error) {
810
- // If room doesn't exist, create it
811
- const roomData = {
812
- id: roomId, // Provide the ID directly for comments
813
- title: `Comments for ${entityId}:${instanceId}`,
814
- members: [this.getCurrentUser()],
815
- };
816
- // Create a new room or return the ID if it already exists
817
- try {
818
- const result = await this.createRoom(roomData);
819
- return typeof result === 'string' ? result : roomId;
820
- }
821
- catch (e) {
822
- // If we get a duplicate error, the room was created by another request
823
- return roomId;
824
- }
777
+ //#endregion
778
+ //#region ---- Comment Thread Service Implementation ----
779
+ async createThread(entityRef, topic, title) {
780
+ const currentUser = this.getCurrentUser();
781
+ const room = {
782
+ type: 'comment',
783
+ topic: topic,
784
+ title: title || `Comments for ${entityRef.type}#${entityRef.id}`,
785
+ members: [currentUser],
786
+ entityRef: entityRef,
787
+ createdBy: currentUser.id,
788
+ createdAt: new Date(),
789
+ };
790
+ await this.roomService.insertOne(room);
791
+ const newThread = await this.getThread(entityRef, topic);
792
+ if (!newThread) {
793
+ throw new Error('Failed to create or retrieve thread after creation.');
825
794
  }
795
+ this._threadAdded$.next(newThread);
796
+ return newThread;
826
797
  }
827
- // === Comment Operations ===
828
- async query(queryRequest) {
829
- const { params, skip = 0, take = 20 } = queryRequest;
830
- const { entityId, instanceId, roomType = 'default' } = params || {};
831
- if (!entityId || !instanceId) {
832
- throw new Error('EntityId and InstanceId are required for querying comments');
833
- }
834
- const roomId = this.formatRoomId(entityId, instanceId, roomType);
835
- // Fetch messages for this room
836
- const result = await this.messageService.query({
837
- skip,
838
- take,
798
+ async getThread(entityRef, topic) {
799
+ const { items } = await this.roomService.query({
800
+ skip: 0,
801
+ take: 1,
839
802
  filter: {
840
- field: 'roomId',
841
- value: roomId,
842
- operator: { type: 'equal' },
803
+ logic: 'and',
804
+ filters: [
805
+ {
806
+ field: 'entityRef.id',
807
+ value: entityRef.id,
808
+ operator: this.filterService.getOperator('equal'),
809
+ },
810
+ {
811
+ field: 'entityRef.type',
812
+ value: entityRef.type,
813
+ operator: this.filterService.getOperator('equal'),
814
+ },
815
+ {
816
+ field: 'topic',
817
+ value: topic,
818
+ operator: this.filterService.getOperator('equal'),
819
+ },
820
+ ],
843
821
  },
844
822
  });
845
- // Transform messages to comments
846
- const comments = await Promise.all(result.items.map((message) => this.formatComment(message)));
847
- // Build comment hierarchy
848
- const hierarchicalComments = await this.buildCommentHierarchy(comments);
823
+ const room = items[0];
824
+ if (!room) {
825
+ return null;
826
+ }
827
+ const comments = await this.getComments(room.id, true);
849
828
  return {
850
- items: hierarchicalComments,
851
- total: result.total,
829
+ ...room,
830
+ comments,
831
+ commentCount: comments.length,
852
832
  };
853
833
  }
854
- async getOne(id) {
855
- const message = await this.messageService.getOne(id);
856
- return this.formatComment(message);
834
+ async listThreads(entityRef, topic) {
835
+ const { items } = await this.roomService.query({
836
+ skip: 0,
837
+ take: 100,
838
+ filter: {
839
+ logic: 'and',
840
+ filters: [
841
+ {
842
+ field: 'entityRef.id',
843
+ value: entityRef.id,
844
+ operator: this.filterService.getOperator('equal'),
845
+ },
846
+ {
847
+ field: 'entityRef.type',
848
+ value: entityRef.type,
849
+ operator: this.filterService.getOperator('equal'),
850
+ },
851
+ {
852
+ field: 'topic',
853
+ value: topic,
854
+ operator: this.filterService.getOperator('equal'),
855
+ },
856
+ ],
857
+ },
858
+ });
859
+ const threads = await Promise.all(items.map((room) => this.getThread(room.entityRef, room.topic)));
860
+ return threads.filter((t) => t !== null);
857
861
  }
858
- async insertOne(commentData) {
859
- const { entityId, instanceId, roomType = 'default', content, contentType = 'text', replyId, isPrivate, } = commentData;
860
- // Ensure the room exists
861
- const roomId = await this.ensureRoomExists(entityId, instanceId, roomType);
862
- // Create the message content
863
- const messageContent = {
864
- content,
865
- contentType,
866
- };
867
- // Create the message object
862
+ async updateThread(entityRef, topic, data) {
863
+ const thread = await this.getThread(entityRef, topic);
864
+ if (!thread) {
865
+ throw new Error(`Thread for entity ${entityRef.type}#${entityRef.id} not found.`);
866
+ }
867
+ await this.roomService.updateOne(thread.id, data);
868
+ const updatedThread = await this.getThread(entityRef, topic);
869
+ if (!updatedThread) {
870
+ throw new Error('Failed to retrieve thread after update.');
871
+ }
872
+ return updatedThread;
873
+ }
874
+ async deleteThread(entityRef, topic) {
875
+ const thread = await this.getThread(entityRef, topic);
876
+ if (thread) {
877
+ await this.roomService.deleteOne(thread.id);
878
+ this._threadRemoved$.next(thread.id);
879
+ }
880
+ return true;
881
+ }
882
+ //#endregion
883
+ //#region ---- Comment Service Implementation ----
884
+ async addComment(threadId, content, parentCommentId, contentType = 'text', userId) {
885
+ const author = userId ? await this.getUserInfo(userId) : this.getCurrentUser();
886
+ const messageContent = { content, contentType };
868
887
  const message = {
869
- roomId,
888
+ roomId: threadId,
870
889
  message: messageContent,
871
- author: this.getCurrentUser(),
872
- replyId: replyId || null,
890
+ author,
891
+ replyId: parentCommentId,
873
892
  reactions: [],
874
893
  seen: [],
894
+ createdBy: author.id,
895
+ createdAt: new Date(),
875
896
  };
876
- // Insert the message
877
897
  const messageId = await this.messageService.insertOne(message);
878
- // Fetch the full message to return as a comment entity
879
- const insertedMessage = await this.messageService.getOne(messageId);
880
- return this.formatComment(insertedMessage);
881
- }
882
- async updateOne(id, update) {
883
- // If content is being updated, format it correctly
884
- let messageUpdate = {};
885
- if (update.message?.content) {
886
- messageUpdate.message = {
887
- content: update.message.content,
888
- contentType: update.message.contentType || 'text',
889
- };
890
- }
891
- // Apply the update
892
- const result = await this.messageService.updateOne(id, messageUpdate);
893
- // Return as a comment entity
894
- return this.formatComment(result);
898
+ const newComment = await this.getComment(messageId);
899
+ this._commentAdded$.next(newComment);
900
+ return newComment;
895
901
  }
896
- async deleteOne(id) {
897
- try {
898
- await this.messageService.deleteOne(id);
899
- return true;
900
- }
901
- catch (error) {
902
- console.error('Failed to delete comment:', error);
903
- return false;
902
+ async getComments(threadId, nested = true) {
903
+ const { items } = await this.messageService.query({
904
+ skip: 0,
905
+ filter: { field: 'roomId', value: threadId, operator: this.filterService.getOperator('equal') },
906
+ sort: [{ field: 'createdAt', dir: 'asc' }],
907
+ take: 500, // Assuming a reasonable limit for comments per thread
908
+ });
909
+ const formattedComments = items.map((c) => this.formatComment(c));
910
+ if (nested) {
911
+ return this.buildCommentTree(formattedComments);
904
912
  }
913
+ return formattedComments;
914
+ }
915
+ async getComment(commentId) {
916
+ const message = await this.messageService.getOne(commentId);
917
+ return this.formatComment(message);
905
918
  }
906
- // === Comment-Specific Operations ===
907
- async like(messageId) {
919
+ async editComment(commentId, content, contentType = 'text') {
920
+ const messageContent = { content, contentType };
921
+ await this.messageService.updateOne(commentId, {
922
+ message: messageContent,
923
+ updatedAt: new Date(),
924
+ updatedBy: this.getCurrentUser().id,
925
+ });
926
+ const updatedComment = await this.getComment(commentId);
927
+ this._commentUpdated$.next(updatedComment);
928
+ return updatedComment;
929
+ }
930
+ async deleteComment(commentId) {
931
+ // Note: This is a hard delete. Soft delete might be preferable.
932
+ await this.messageService.deleteOne(commentId);
933
+ this._commentRemoved$.next(commentId);
934
+ return true;
935
+ }
936
+ //#endregion
937
+ //#region ---- Comment Reaction Service Implementation ----
938
+ async addReaction(commentId, type) {
908
939
  const author = this.getCurrentUser();
909
- const message = await this.messageService.getOne(messageId);
910
- // Check if the user has already liked this message
911
- const reactions = message.reactions || [];
912
- const existingReaction = reactions.find((r) => r.author.id === author.id && r.type === 'like');
913
- let updatedReactions;
914
- if (existingReaction) {
915
- // If the reaction exists, remove it (unlike)
916
- updatedReactions = reactions.filter((r) => !(r.author.id === author.id && r.type === 'like'));
917
- }
918
- else {
919
- // If the reaction doesn't exist, add it (like)
920
- updatedReactions = [...reactions, { author, type: 'like' }];
940
+ const comment = await this.messageService.getOne(commentId);
941
+ const reactions = comment.reactions || [];
942
+ if (reactions.some((r) => r.author.id === author.id && r.type === type)) {
943
+ return this.formatComment(comment);
921
944
  }
922
- // Update the message with the new reactions
923
- const updatedMessage = await this.messageService.updateOne(messageId, { reactions: updatedReactions });
924
- // Return as a comment entity
925
- return this.formatComment(updatedMessage);
945
+ const updatedReactions = [...reactions, { author, type }];
946
+ await this.messageService.updateOne(commentId, { reactions: updatedReactions });
947
+ const updatedComment = await this.getComment(commentId);
948
+ this._commentUpdated$.next(updatedComment);
949
+ return updatedComment;
926
950
  }
927
- async getCommentCount(entityId, instanceId, roomType = 'default') {
928
- const roomId = this.formatRoomId(entityId, instanceId, roomType);
929
- try {
930
- const result = await this.messageService.query({
931
- skip: 0,
932
- take: 1,
933
- filter: {
934
- field: 'roomId',
935
- value: roomId,
936
- operator: { type: 'equal' },
937
- },
938
- });
939
- return result.total;
940
- }
941
- catch (error) {
942
- console.error('Failed to get comment count:', error);
943
- return 0;
944
- }
951
+ async removeReaction(commentId, type) {
952
+ const author = this.getCurrentUser();
953
+ const comment = await this.messageService.getOne(commentId);
954
+ const updatedReactions = (comment.reactions || []).filter((r) => !(r.author.id === author.id && r.type === type));
955
+ await this.messageService.updateOne(commentId, { reactions: updatedReactions });
956
+ const updatedComment = await this.getComment(commentId);
957
+ this._commentUpdated$.next(updatedComment);
958
+ return updatedComment;
945
959
  }
946
- async getAllCommentsWithReplies(entityId, instanceId, roomType = 'default', skip = 0, take = 100) {
947
- const queryRequest = {
948
- params: { entityId, instanceId, roomType },
949
- skip,
950
- take,
951
- };
952
- return this.query(queryRequest);
960
+ async getReactions(commentId) {
961
+ const comment = await this.messageService.getOne(commentId);
962
+ return comment.reactions || [];
953
963
  }
954
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
955
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, providedIn: 'root' }); }
956
- }
957
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, decorators: [{
958
- type: Injectable,
959
- args: [{
960
- providedIn: 'root',
961
- }]
962
- }] });
963
-
964
- class AXMCommentLookupPopup extends AXBasePageComponent {
965
- constructor() {
966
- super(...arguments);
967
- this.lookupNode = {
968
- name: 'form-field',
969
- path: 'form-field',
970
- type: 'form-field',
971
- children: [
972
- {
973
- name: 'lookup',
974
- path: 'lookup',
975
- type: 'lookup-editor',
976
- options: {
977
- entity: 'SecurityManagement.users',
978
- multiple: true,
979
- },
980
- },
981
- ],
982
- options: {
983
- label: 'Select users:',
984
- },
985
- };
986
- this.titleNode = {
987
- name: 'form-field',
988
- path: 'form-field',
989
- type: 'form-field',
990
- children: [
991
- {
992
- name: 'title',
993
- path: 'title',
994
- type: 'text-editor',
995
- options: {
996
- placeholder: 'Enter title',
997
- required: true,
998
- },
999
- },
1000
- ],
1001
- options: {
1002
- label: 'Title:',
1003
- },
1004
- };
1005
- this.context = signal({});
964
+ async likeComment(commentId) {
965
+ return this.addReaction(commentId, 'like');
1006
966
  }
1007
- handleClose() {
1008
- this.close({
1009
- lookup: this.context()?.lookup,
1010
- title: this.context()?.title,
1011
- });
967
+ async unlikeComment(commentId) {
968
+ return this.removeReaction(commentId, 'like');
1012
969
  }
1013
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentLookupPopup, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1014
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentLookupPopup, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `<axp-widgets-container [context]="context()" (onContextChanged)="context.set($event.data)">
1015
- <div class="ax-p-4 ax-flex ax-flex-col ax-gap-4">
1016
- <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
1017
- @if (context()?.lookup?.length > 1) {
1018
- <ng-container axp-widget-renderer [node]="titleNode" [mode]="'edit'"> </ng-container>
1019
- }
1020
- </div>
1021
- </axp-widgets-container>
1022
-
1023
- <ax-footer>
1024
- <ax-suffix>
1025
- <ax-button
1026
- text="Accept & Send"
1027
- color="primary"
1028
- [disabled]="context()?.lookup?.length > 1 && !context()?.title"
1029
- (onClick)="handleClose()"
1030
- ></ax-button>
1031
- </ax-suffix>
1032
- </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.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i2.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }] }); }
970
+ //#endregion
971
+ //#region ---- Comment Pin Service Implementation ----
972
+ async pinComment(commentId) {
973
+ await this.messageService.updateOne(commentId, { isPinned: true });
974
+ const updatedComment = await this.getComment(commentId);
975
+ this._commentUpdated$.next(updatedComment);
976
+ return updatedComment;
977
+ }
978
+ async unpinComment(commentId) {
979
+ await this.messageService.updateOne(commentId, { isPinned: false });
980
+ const updatedComment = await this.getComment(commentId);
981
+ this._commentUpdated$.next(updatedComment);
982
+ return updatedComment;
983
+ }
984
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
985
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentServiceImpl, providedIn: 'root' }); }
1033
986
  }
1034
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentLookupPopup, decorators: [{
1035
- type: Component,
987
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentServiceImpl, decorators: [{
988
+ type: Injectable,
1036
989
  args: [{
1037
- template: `<axp-widgets-container [context]="context()" (onContextChanged)="context.set($event.data)">
1038
- <div class="ax-p-4 ax-flex ax-flex-col ax-gap-4">
1039
- <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
1040
- @if (context()?.lookup?.length > 1) {
1041
- <ng-container axp-widget-renderer [node]="titleNode" [mode]="'edit'"> </ng-container>
1042
- }
1043
- </div>
1044
- </axp-widgets-container>
1045
-
1046
- <ax-footer>
1047
- <ax-suffix>
1048
- <ax-button
1049
- text="Accept & Send"
1050
- color="primary"
1051
- [disabled]="context()?.lookup?.length > 1 && !context()?.title"
1052
- (onClick)="handleClose()"
1053
- ></ax-button>
1054
- </ax-suffix>
1055
- </ax-footer>`,
1056
- imports: [AXDecoratorModule, AXPLayoutBuilderModule, AXButtonModule],
990
+ providedIn: 'root',
1057
991
  }]
1058
992
  }] });
1059
993
 
1060
- class AXMCommentListViewComponent {
994
+ class AXMCommentComponent {
1061
995
  constructor() {
996
+ this.refrenceType = input.required();
997
+ this.refrenceId = input.required();
998
+ this.subject = input.required();
1062
999
  this.hasCooldown = signal(false);
1063
1000
  this.commentContent = signal('');
1064
1001
  this.isSubmitting = signal(false);
@@ -1068,34 +1005,23 @@ class AXMCommentListViewComponent {
1068
1005
  this.failedImageIds = signal([]);
1069
1006
  this.activeReplyComment = signal(undefined);
1070
1007
  this.activeEditComment = signal(undefined);
1008
+ this.highlightedCommentId = signal(null);
1071
1009
  this.wysiwygEditor = viewChild.required('w');
1072
1010
  this.commentService = inject(AXMCommentService);
1073
- this.platform = inject(AXPlatform);
1074
- this.route = inject(ActivatedRoute);
1075
- this.popupService = inject(AXPopupService);
1011
+ this.sessionService = inject(AXPSessionService);
1076
1012
  this.toastService = inject(AXToastService);
1077
1013
  this.dialogService = inject(AXDialogService);
1078
1014
  this.sanitize = inject(DomSanitizer);
1079
- this.routeParams = this.route.snapshot;
1080
- this.getPayload = computed(() => ({
1081
- params: this.payload(),
1082
- skip: 0,
1083
- take: 10,
1084
- }));
1085
- this.payload = computed(() => ({
1086
- roomType: 'default',
1087
- entityId: this.routeParams.params?.['module'] + '.' + this.routeParams.params?.['entity'],
1088
- instanceId: this.routeParams.params?.['id'],
1089
- }));
1015
+ this.destroyRef = inject(DestroyRef);
1090
1016
  this.comments = signal([]);
1091
- this.wysiwyg = viewChild('w');
1017
+ this.thread = signal(undefined);
1092
1018
  this.wysiwygOptions = signal({
1093
1019
  look: 'solid',
1094
1020
  });
1095
1021
  this.avatarConfig = signal({
1096
1022
  color: 'primary',
1097
1023
  look: 'rounded',
1098
- type: 'solid', // 'image' | 'text' | 'icon' | 'default'
1024
+ type: 'solid',
1099
1025
  });
1100
1026
  this.validateContent = (content) => {
1101
1027
  let isValid = true;
@@ -1111,39 +1037,84 @@ class AXMCommentListViewComponent {
1111
1037
  };
1112
1038
  }
1113
1039
  ngOnInit() {
1114
- this.loadComments();
1115
- }
1116
- sanitizeHtml(htmlContent) {
1117
- if (!htmlContent)
1118
- return this.sanitize.bypassSecurityTrustHtml('');
1119
- return this.sanitize.bypassSecurityTrustHtml(htmlContent);
1120
- }
1121
- handleImageError(imageId) {
1122
- this.failedImageIds.update((ids) => [...ids, imageId]);
1123
- }
1124
- checkImageExists(imageId) {
1125
- return !this.failedImageIds().includes(imageId);
1126
- }
1127
- extractInitials(name) {
1128
- if (!name)
1129
- return '?';
1130
- // Handle the case where name is an object with fullName property
1131
- const nameStr = typeof name === 'object' && name.fullName ? name.fullName : String(name);
1132
- const words = nameStr.split(' ');
1133
- const initials = words.map((word) => word.charAt(0).toUpperCase());
1134
- return initials.join('');
1135
- }
1136
- async loadComments() {
1137
1040
  this.isLoading.set(true);
1138
- try {
1139
- const response = await this.commentService.query(this.getPayload());
1140
- this.comments.set(response.items);
1141
- }
1142
- catch (error) {
1143
- console.error('Failed to load comments:', error);
1144
- this.toastService.show({
1145
- content: 'Failed to load comments. Please try again.',
1146
- color: 'danger',
1041
+ this.loadComments().finally(() => {
1042
+ setTimeout(() => {
1043
+ this.isLoading.set(false);
1044
+ }, 250);
1045
+ });
1046
+ this.commentService.commentAdded$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((comment) => {
1047
+ if (comment.replyId) {
1048
+ this.comments.update((comments) => {
1049
+ return comments.map((c) => {
1050
+ if (c.id === comment.replyId) {
1051
+ const newReplies = c.replies ? [...c.replies, comment] : [comment];
1052
+ return { ...c, replies: newReplies, replyCount: newReplies.length };
1053
+ }
1054
+ return c;
1055
+ });
1056
+ });
1057
+ }
1058
+ else {
1059
+ this.comments.update((comments) => [...comments, comment]);
1060
+ }
1061
+ });
1062
+ this.commentService.commentUpdated$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((updatedComment) => {
1063
+ const update = (list) => {
1064
+ return list.map((c) => {
1065
+ if (c.id === updatedComment.id) {
1066
+ return { ...c, ...updatedComment, replies: updatedComment.replies || c.replies };
1067
+ }
1068
+ if (c.replies && c.replies.length > 0) {
1069
+ return { ...c, replies: update(c.replies) };
1070
+ }
1071
+ return c;
1072
+ });
1073
+ };
1074
+ this.comments.update((comments) => update(comments));
1075
+ });
1076
+ }
1077
+ getCurrentUser() {
1078
+ const user = this.sessionService.user;
1079
+ return user ? { id: user.id, type: 'user' } : null;
1080
+ }
1081
+ isLikedByUser(comment) {
1082
+ const user = this.getCurrentUser();
1083
+ if (!user)
1084
+ return false;
1085
+ return (comment.reactions || []).some((r) => r.author.id === user.id && r.type === 'like');
1086
+ }
1087
+ getReactionsCount(comment) {
1088
+ return (comment.reactions || []).filter((r) => r.type === 'like').length;
1089
+ }
1090
+ sanitizeHtml(htmlContent) {
1091
+ if (!htmlContent)
1092
+ return this.sanitize.bypassSecurityTrustHtml('');
1093
+ return this.sanitize.bypassSecurityTrustHtml(htmlContent);
1094
+ }
1095
+ handleImageError(imageId) {
1096
+ this.failedImageIds.update((ids) => [...ids, imageId]);
1097
+ }
1098
+ checkImageExists(imageId) {
1099
+ return !this.failedImageIds().includes(imageId);
1100
+ }
1101
+ async loadComments() {
1102
+ try {
1103
+ const thread = await this.commentService.getThread({ type: this.refrenceType(), id: this.refrenceId() }, this.subject());
1104
+ if (thread) {
1105
+ this.thread.set(thread);
1106
+ this.comments.set(thread.comments);
1107
+ }
1108
+ else {
1109
+ this.comments.set([]);
1110
+ this.thread.set(undefined);
1111
+ }
1112
+ }
1113
+ catch (error) {
1114
+ console.error('Failed to load comments:', error);
1115
+ this.toastService.show({
1116
+ content: 'Failed to load comments. Please try again.',
1117
+ color: 'danger',
1147
1118
  location: 'bottom-center',
1148
1119
  closeButton: true,
1149
1120
  timeOut: 3000,
@@ -1151,36 +1122,31 @@ class AXMCommentListViewComponent {
1151
1122
  });
1152
1123
  this.comments.set([]);
1153
1124
  }
1154
- finally {
1155
- setTimeout(() => {
1156
- this.isLoading.set(false);
1157
- }, 250);
1158
- }
1159
1125
  }
1160
- editMessage(comment, reply) {
1126
+ editMessage(comment, reply, userName) {
1161
1127
  this.isReplyingMode.set(false);
1162
1128
  this.activeReplyComment.set(undefined);
1163
1129
  this.isEditingMode.set(true);
1164
- this.activeEditComment.set(comment);
1130
+ this.activeEditComment.set({ ...comment, userName: userName || '' });
1165
1131
  const contentToEdit = reply ? reply.message?.content : comment.message?.content;
1166
1132
  this.commentContent.set(contentToEdit || '');
1167
1133
  this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
1168
1134
  }
1169
- replyMessage(comment, reply) {
1135
+ replyMessage(comment, reply, userName) {
1170
1136
  this.isEditingMode.set(false);
1171
1137
  this.activeEditComment.set(undefined);
1172
1138
  this.isReplyingMode.set(true);
1173
- this.activeReplyComment.set(comment);
1139
+ this.activeReplyComment.set({ ...comment, userName: userName || '' });
1174
1140
  if (reply) {
1175
1141
  const author = reply.author.fullName || reply.author.id;
1176
1142
  const mention = `<a data-id="${reply.id}">@${author}</a> `;
1177
1143
  this.commentContent.set(mention);
1178
- this.wysiwyg()?.focus();
1179
1144
  document.getElementsByClassName('ql-editor')[0].innerHTML = mention;
1145
+ this.wysiwygEditor()?.focus();
1180
1146
  }
1181
1147
  this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
1182
1148
  }
1183
- async deleteComment(comment) {
1149
+ async deleteComment(commentId) {
1184
1150
  const dialog = this.dialogService.open({
1185
1151
  icon: 'fa-regular fa-warning',
1186
1152
  content: 'Are you sure you want to delete this comment?',
@@ -1197,77 +1163,20 @@ class AXMCommentListViewComponent {
1197
1163
  e.source.disabled = true;
1198
1164
  e.source.loading = true;
1199
1165
  try {
1200
- if (comment.id) {
1201
- await this.commentService.deleteOne(comment.id);
1202
- this.removeMessageById(comment.id);
1203
- this.toastService.show({
1204
- content: 'Comment deleted successfully.',
1205
- color: 'success',
1206
- location: 'bottom-center',
1207
- closeButton: true,
1208
- timeOut: 3000,
1209
- timeOutProgress: true,
1210
- });
1211
- if (this.isEditingMode() && this.activeEditComment()?.id === comment.id) {
1212
- this.resetReplyEditState();
1213
- }
1214
- dialog.close();
1215
- }
1216
- }
1217
- catch (error) {
1166
+ await this.commentService.deleteComment(commentId);
1167
+ this.removeMessageById(commentId);
1218
1168
  this.toastService.show({
1219
- content: typeof error === 'string' ? error : 'Failed to delete comment!',
1220
- color: 'danger',
1169
+ content: 'Comment deleted successfully.',
1170
+ color: 'success',
1221
1171
  location: 'bottom-center',
1222
1172
  closeButton: true,
1223
1173
  timeOut: 3000,
1224
1174
  timeOutProgress: true,
1225
1175
  });
1226
- }
1227
- },
1228
- },
1229
- {
1230
- text: 'Cancel',
1231
- color: 'default',
1232
- autofocus: true,
1233
- onClick: (e) => {
1234
- dialog.close();
1235
- },
1236
- },
1237
- ],
1238
- closeButton: false,
1239
- });
1240
- }
1241
- async deleteReply(comment, reply) {
1242
- const dialog = this.dialogService.open({
1243
- icon: 'fa-regular fa-warning',
1244
- content: 'Are you sure you want to delete this reply?',
1245
- title: 'Delete Reply',
1246
- type: 'danger',
1247
- orientation: 'horizontal',
1248
- buttons: [
1249
- {
1250
- text: 'Delete',
1251
- color: 'danger',
1252
- onClick: async (e) => {
1253
- e.handled = true;
1254
- e.source.text = 'Deleting...';
1255
- e.source.disabled = true;
1256
- e.source.loading = true;
1257
- try {
1258
- if (reply.id) {
1259
- await this.commentService.deleteOne(reply.id);
1260
- this.removeMessageById(comment.id, reply.id);
1261
- this.toastService.show({
1262
- content: 'Comment deleted successfully.',
1263
- color: 'success',
1264
- location: 'bottom-center',
1265
- closeButton: true,
1266
- timeOut: 3000,
1267
- timeOutProgress: true,
1268
- });
1269
- dialog.close();
1176
+ if (this.isEditingMode() && this.activeEditComment()?.id === commentId) {
1177
+ this.resetReplyEditState();
1270
1178
  }
1179
+ dialog.close();
1271
1180
  }
1272
1181
  catch (error) {
1273
1182
  this.toastService.show({
@@ -1301,130 +1210,83 @@ class AXMCommentListViewComponent {
1301
1210
  this.commentContent.set('');
1302
1211
  document.getElementsByClassName('ql-editor')[0].innerHTML = '';
1303
1212
  }
1304
- async toggleLike(comment, reply) {
1213
+ async toggleLike(comment) {
1214
+ const isLiked = this.isLikedByUser(comment);
1215
+ this.updateLikeStatus(comment.id, !isLiked);
1305
1216
  try {
1306
- if (reply) {
1307
- if (reply.id) {
1308
- this.updateLikeStatus(comment.id, reply.id);
1309
- await this.commentService.like(reply.id);
1310
- }
1217
+ if (isLiked) {
1218
+ await this.commentService.unlikeComment(comment.id);
1311
1219
  }
1312
1220
  else {
1313
- if (comment.id) {
1314
- this.updateLikeStatus(comment.id);
1315
- await this.commentService.like(comment.id);
1316
- }
1221
+ await this.commentService.likeComment(comment.id);
1317
1222
  }
1318
1223
  }
1319
1224
  catch (error) {
1320
1225
  console.error('Failed to toggle like:', error);
1321
- // Revert the optimistic update if the server call fails
1322
- if (reply && reply.id) {
1323
- this.updateLikeStatus(comment.id, reply.id);
1324
- }
1325
- else if (comment.id) {
1326
- this.updateLikeStatus(comment.id);
1327
- }
1328
- }
1329
- }
1330
- updateLikeStatus(commentId, replyId) {
1331
- this.comments.update((commentsList) => {
1332
- return commentsList.map((comment) => {
1333
- if (comment.id === commentId) {
1334
- if (replyId && comment.replies) {
1335
- const updatedReplies = comment.replies.map((reply) => reply.id === replyId
1336
- ? {
1337
- ...reply,
1338
- isLiked: !reply.isLiked,
1339
- reactionsCount: reply.isLiked && reply.reactionsCount !== undefined
1340
- ? reply.reactionsCount - 1
1341
- : (reply.reactionsCount || 0) + 1,
1342
- }
1343
- : reply);
1344
- return {
1345
- ...comment,
1346
- replies: updatedReplies,
1347
- };
1226
+ this.updateLikeStatus(comment.id, isLiked); // Revert optimistic update
1227
+ }
1228
+ }
1229
+ updateLikeStatus(commentId, willBeLiked) {
1230
+ const findAndToggle = (comments) => {
1231
+ return comments.map((c) => {
1232
+ if (c.id === commentId) {
1233
+ const user = this.getCurrentUser();
1234
+ if (!user)
1235
+ return c;
1236
+ let reactions = c.reactions || [];
1237
+ if (willBeLiked) {
1238
+ reactions.push({ author: user, type: 'like' });
1348
1239
  }
1349
1240
  else {
1350
- return {
1351
- ...comment,
1352
- isLiked: !comment.isLiked,
1353
- reactionsCount: comment.isLiked && comment.reactionsCount !== undefined
1354
- ? comment.reactionsCount - 1
1355
- : (comment.reactionsCount || 0) + 1,
1356
- };
1241
+ reactions = reactions.filter((r) => r.author.id !== user.id || r.type !== 'like');
1357
1242
  }
1243
+ return { ...c, reactions };
1244
+ }
1245
+ if (c.replies && c.replies.length > 0) {
1246
+ c.replies = findAndToggle(c.replies);
1358
1247
  }
1359
- return comment;
1248
+ return c;
1360
1249
  });
1361
- });
1362
- }
1363
- removeMessageById(commentId, replyId) {
1364
- if (replyId) {
1365
- // Remove a reply from a comment
1366
- this.comments.update((commentsList) => commentsList.map((comment) => {
1367
- if (comment.id === commentId && comment.replies) {
1368
- return {
1369
- ...comment,
1370
- replies: comment.replies.filter((reply) => reply.id !== replyId),
1371
- };
1250
+ };
1251
+ this.comments.update((comments) => findAndToggle(comments));
1252
+ }
1253
+ removeMessageById(commentId) {
1254
+ const filterOut = (comments) => {
1255
+ return comments
1256
+ .filter((c) => c.id !== commentId)
1257
+ .map((c) => {
1258
+ if (c.replies && c.replies.length > 0) {
1259
+ c.replies = filterOut(c.replies);
1372
1260
  }
1373
- return comment;
1374
- }));
1375
- }
1376
- else {
1377
- // Remove a whole comment
1378
- this.comments.update((commentsList) => commentsList.filter((comment) => comment.id !== commentId));
1379
- }
1261
+ return c;
1262
+ });
1263
+ };
1264
+ this.comments.update((comments) => filterOut(comments));
1380
1265
  }
1381
- async submitComment(isPrivate = false) {
1266
+ async submitComment() {
1382
1267
  if (!this.validateContent(this.commentContent()).result) {
1383
1268
  return;
1384
1269
  }
1385
1270
  this.isSubmitting.set(true);
1386
- let memberLookup;
1387
- if (isPrivate) {
1388
- const popupConfig = {
1389
- header: true,
1390
- size: 'md',
1391
- draggable: true,
1392
- hasBackdrop: true,
1393
- closeButton: true,
1394
- closeOnBackdropClick: false,
1395
- };
1396
- const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
1397
- memberLookup = popup.data?.lookup;
1398
- }
1271
+ console.log(this.commentContent());
1399
1272
  try {
1273
+ let currentThread = this.thread();
1274
+ if (!currentThread) {
1275
+ currentThread = await this.commentService.createThread({ type: this.refrenceType(), id: this.refrenceId() }, this.subject());
1276
+ this.thread.set(currentThread);
1277
+ }
1400
1278
  if (this.isEditingMode() && this.activeEditComment()?.id) {
1401
- const payload = {
1402
- message: {
1403
- content: this.commentContent(),
1404
- contentType: 'text',
1405
- },
1406
- };
1407
- await this.commentService.updateOne(this.activeEditComment().id, payload);
1279
+ await this.commentService.editComment(this.activeEditComment().id, this.commentContent());
1408
1280
  this.hasCooldown.set(true);
1409
1281
  this.isEditingMode.set(false);
1410
1282
  this.activeEditComment.set(undefined);
1411
1283
  }
1412
1284
  else {
1413
- const payload = {
1414
- ...this.payload(),
1415
- content: this.commentContent(),
1416
- contentType: 'text',
1417
- isPrivate: isPrivate,
1418
- replyId: this.activeReplyComment()?.id ?? null,
1419
- };
1420
- await this.commentService.insertOne(payload);
1285
+ await this.commentService.addComment(currentThread.id, this.commentContent(), this.activeReplyComment()?.id);
1421
1286
  this.hasCooldown.set(true);
1422
1287
  this.isReplyingMode.set(false);
1423
1288
  this.activeReplyComment.set(undefined);
1424
1289
  }
1425
- // Reload comments to get the updated list with proper hierarchy
1426
- const response = await this.commentService.query(this.getPayload());
1427
- this.comments.set(response.items);
1428
1290
  this.commentContent.set('');
1429
1291
  document.getElementsByClassName('ql-editor')[0].innerHTML = '';
1430
1292
  setTimeout(() => {
@@ -1452,28 +1314,33 @@ class AXMCommentListViewComponent {
1452
1314
  const el = document.getElementById(comment.id);
1453
1315
  if (el) {
1454
1316
  el.scrollIntoView({ behavior: 'smooth', block: 'center' });
1455
- const content = el?.firstElementChild?.children[1];
1456
- if (content) {
1457
- const prevBg = content.style.background;
1458
- content.style.borderRadius = '0.25rem';
1459
- content.style.transition = 'background 1s ease-in-out';
1460
- content.style.background = `rgba(var(--ax-color-on-surface), var(--tw-bg-opacity))`;
1461
- setTimeout(() => {
1462
- content.style.background = prevBg || 'rgba(0, 0, 0, 0)';
1463
- }, 1000);
1464
- }
1317
+ this.highlightedCommentId.set(comment.id);
1318
+ setTimeout(() => {
1319
+ this.highlightedCommentId.set(null);
1320
+ }, 1000);
1465
1321
  }
1466
1322
  }
1467
1323
  }
1468
- calcDefrenetTime(date) {
1469
- return date ? Date.now() - date.getTime() : undefined;
1470
- }
1471
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1472
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentListViewComponent, isStandalone: true, 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: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i1.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i1.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i5$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i6.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i7.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i9.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i10.AXFormatPipe, name: "format" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i11.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i11.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i11.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "component", type: i12.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i14.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1324
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1325
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: AXMCommentComponent, isStandalone: true, selector: "axm-comment", inputs: { refrenceType: { classPropertyName: "refrenceType", publicName: "refrenceType", isSignal: true, isRequired: true, transformFunction: null }, refrenceId: { classPropertyName: "refrenceId", publicName: "refrenceId", isSignal: true, isRequired: true, transformFunction: null }, subject: { classPropertyName: "subject", publicName: "subject", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-size-full ax-flex ax-flex-col ax-justify-between ax-p-4 ax-overflow-hidden\">\n <ax-comment-container class=\"ax-overflow-auto\">\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item\n [id]=\"comment.id!\"\n [replyCount]=\"comment.replyCount\"\n [class.highlighted]=\"highlightedCommentId() === comment.id\"\n [@fadeIn]\n >\n <axp-user-avatar #user [size]=\"48\" [userId]=\"comment.author.id\" ngProjectAs=\"'ax-avatar'\"></axp-user-avatar>\n <ax-title>{{ user.firstName() + ' ' + user.lastName() }}</ax-title>\n <ax-comment-date>{{ comment.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\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"isLikedByUser(comment)\">\n {{ getReactionsCount(comment) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n ></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\" [class.highlighted]=\"highlightedCommentId() === reply.id\" [@fadeIn]>\n <axp-user-avatar\n #user2\n [size]=\"48\"\n [userId]=\"reply.author.id\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title>{{ user2.firstName() + ' ' + user2.lastName() }}</ax-title>\n <ax-comment-date>{{ reply.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\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(reply.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(reply)\" [liked]=\"isLikedByUser(reply)\">\n {{ getReactionsCount(reply) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n ></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <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-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() ? activeReplyComment()?.userName : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-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()\" 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: [":host{display:block;width:100%;height:100%}ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}ax-wysiwyg-container .highlighted{background:var(--ax-color-primary-050)!important;border-radius:.25rem;transition:background 1s ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i1.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i1.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i5.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i6.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i7.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i8.AXFormatPipe, name: "format" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXFormModule }, { 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: "ngmodule", type: AXToolBarModule }, { kind: "component", type: i10.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i11.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i11.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i12.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }], animations: [
1326
+ trigger('fadeIn', [
1327
+ transition(':enter', [
1328
+ style({ opacity: 0, transform: 'translateY(10px)' }),
1329
+ animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
1330
+ ]),
1331
+ ]),
1332
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1473
1333
  }
1474
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentListViewComponent, decorators: [{
1334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentComponent, decorators: [{
1475
1335
  type: Component,
1476
- args: [{ selector: 'axm-comment-list-view', imports: [
1336
+ args: [{ selector: 'axm-comment', animations: [
1337
+ trigger('fadeIn', [
1338
+ transition(':enter', [
1339
+ style({ opacity: 0, transform: 'translateY(10px)' }),
1340
+ animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
1341
+ ]),
1342
+ ]),
1343
+ ], imports: [
1477
1344
  AXWysiwygModule,
1478
1345
  AXConversationModule,
1479
1346
  AXSkeletonModule,
@@ -1490,106 +1357,356 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
1490
1357
  AXToolBarModule,
1491
1358
  FormsModule,
1492
1359
  AXDropdownButtonModule,
1493
- ], 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"] }]
1360
+ AXPUserAvatarComponent,
1361
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-size-full ax-flex ax-flex-col ax-justify-between ax-p-4 ax-overflow-hidden\">\n <ax-comment-container class=\"ax-overflow-auto\">\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item\n [id]=\"comment.id!\"\n [replyCount]=\"comment.replyCount\"\n [class.highlighted]=\"highlightedCommentId() === comment.id\"\n [@fadeIn]\n >\n <axp-user-avatar #user [size]=\"48\" [userId]=\"comment.author.id\" ngProjectAs=\"'ax-avatar'\"></axp-user-avatar>\n <ax-title>{{ user.firstName() + ' ' + user.lastName() }}</ax-title>\n <ax-comment-date>{{ comment.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\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"isLikedByUser(comment)\">\n {{ getReactionsCount(comment) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, undefined, user.firstName() + ' ' + user.lastName())\"\n ></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\" [class.highlighted]=\"highlightedCommentId() === reply.id\" [@fadeIn]>\n <axp-user-avatar\n #user2\n [size]=\"48\"\n [userId]=\"reply.author.id\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title>{{ user2.firstName() + ' ' + user2.lastName() }}</ax-title>\n <ax-comment-date>{{ reply.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\n text=\"Edit\"\n color=\"neutral\"\n (click)=\"editMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(reply.id!)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(reply)\" [liked]=\"isLikedByUser(reply)\">\n {{ getReactionsCount(reply) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, reply, user2.firstName() + ' ' + user2.lastName())\"\n ></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <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-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() ? activeReplyComment()?.userName : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-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()\" 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: [":host{display:block;width:100%;height:100%}ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}ax-wysiwyg-container .highlighted{background:var(--ax-color-primary-050)!important;border-radius:.25rem;transition:background 1s ease-in-out}\n"] }]
1494
1362
  }] });
1495
1363
 
1496
- //#region ---- Abstract Chat Service ----
1497
- class AXMChatService {
1364
+ var comment_component = /*#__PURE__*/Object.freeze({
1365
+ __proto__: null,
1366
+ AXMCommentComponent: AXMCommentComponent
1367
+ });
1368
+
1369
+ class AXMCommentPopupComponent extends AXBasePageComponent {
1370
+ constructor() {
1371
+ super(...arguments);
1372
+ this.refrenceType = input.required();
1373
+ this.refrenceId = input.required();
1374
+ this.subject = input.required();
1375
+ }
1376
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentPopupComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1377
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.6", type: AXMCommentPopupComponent, isStandalone: true, selector: "axm-comment-popup", inputs: { refrenceType: { classPropertyName: "refrenceType", publicName: "refrenceType", isSignal: true, isRequired: true, transformFunction: null }, refrenceId: { classPropertyName: "refrenceId", publicName: "refrenceId", isSignal: true, isRequired: true, transformFunction: null }, subject: { classPropertyName: "subject", publicName: "subject", isSignal: true, isRequired: true, transformFunction: null } }, providers: [], usesInheritance: true, ngImport: i0, template: "<ax-content>\n <div class=\"ax-p-6 ax-min-h-[30vh] ax-overflow-y-auto\">\n <axm-comment [refrenceType]=\"refrenceType()\" [refrenceId]=\"refrenceId()\" [subject]=\"subject()\"></axm-comment>\n </div>\n</ax-content>\n<ax-footer>\n <ax-prefix></ax-prefix>\n <ax-suffix>\n <ax-button [text]=\"'close' | translate | async\" look=\"solid\" color=\"primary\" (onClick)=\"close()\"> </ax-button>\n </ax-suffix>\n</ax-footer>\n", dependencies: [{ kind: "ngmodule", type:
1378
+ // Angular
1379
+ CommonModule }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "ngmodule", type:
1380
+ // ACoreX
1381
+ AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }, { kind: "component", type:
1382
+ // Comment
1383
+ AXMCommentComponent, selector: "axm-comment", inputs: ["refrenceType", "refrenceId", "subject"] }], encapsulation: i0.ViewEncapsulation.None }); }
1498
1384
  }
1499
- //#endregion
1500
- class AXMChatServiceImpl {
1385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentPopupComponent, decorators: [{
1386
+ type: Component,
1387
+ args: [{ selector: 'axm-comment-popup', standalone: true, imports: [
1388
+ // Angular
1389
+ CommonModule,
1390
+ // ACoreX
1391
+ AXDecoratorModule,
1392
+ AXButtonModule,
1393
+ AXTranslationModule,
1394
+ // Comment
1395
+ AXMCommentComponent,
1396
+ ], encapsulation: ViewEncapsulation.None, providers: [], template: "<ax-content>\n <div class=\"ax-p-6 ax-min-h-[30vh] ax-overflow-y-auto\">\n <axm-comment [refrenceType]=\"refrenceType()\" [refrenceId]=\"refrenceId()\" [subject]=\"subject()\"></axm-comment>\n </div>\n</ax-content>\n<ax-footer>\n <ax-prefix></ax-prefix>\n <ax-suffix>\n <ax-button [text]=\"'close' | translate | async\" look=\"solid\" color=\"primary\" (onClick)=\"close()\"> </ax-button>\n </ax-suffix>\n</ax-footer>\n" }]
1397
+ }] });
1398
+
1399
+ var commentPopup_component = /*#__PURE__*/Object.freeze({
1400
+ __proto__: null,
1401
+ AXMCommentPopupComponent: AXMCommentPopupComponent
1402
+ });
1403
+
1404
+ class AXMUserLookupPopup extends AXBasePageComponent {
1501
1405
  constructor() {
1502
- this.roomService = inject(AXMRoomService);
1503
- this.messageService = inject(AXMMessageService);
1504
- this.sessionService = inject(AXPSessionService);
1505
- this.usersService = inject(AXMUsersEntityService);
1506
- this._messageSent$ = new Subject();
1507
- this.messageSent$ = this._messageSent$.asObservable();
1508
- }
1509
- //#region ---- Helper Methods ----
1510
- getCurrentUser() {
1511
- const user = this.sessionService.user;
1512
- return {
1513
- id: user?.id || 'guest-user',
1514
- type: 'user',
1406
+ super(...arguments);
1407
+ this.lookupNode = {
1408
+ name: 'form-field',
1409
+ path: 'form-field',
1410
+ type: 'form-field',
1411
+ children: [
1412
+ {
1413
+ name: 'lookup',
1414
+ path: 'lookup',
1415
+ type: 'lookup-editor',
1416
+ options: {
1417
+ entity: 'SecurityManagement.users',
1418
+ multiple: true,
1419
+ },
1420
+ },
1421
+ ],
1422
+ options: {
1423
+ label: 'Select users:',
1424
+ },
1515
1425
  };
1426
+ this.titleNode = {
1427
+ name: 'form-field',
1428
+ path: 'form-field',
1429
+ type: 'form-field',
1430
+ children: [
1431
+ {
1432
+ name: 'title',
1433
+ path: 'title',
1434
+ type: 'text-editor',
1435
+ options: {
1436
+ placeholder: 'Enter title',
1437
+ required: true,
1438
+ },
1439
+ },
1440
+ ],
1441
+ options: {
1442
+ label: 'Title:',
1443
+ },
1444
+ };
1445
+ this.context = signal({});
1516
1446
  }
1517
- async getUserInfo(userId) {
1518
- try {
1519
- const user = await this.usersService.getOne(userId);
1520
- return {
1521
- id: user.id,
1522
- type: 'user',
1523
- };
1524
- }
1525
- catch (error) {
1526
- console.error(`Failed to get user info for ID: ${userId}`, error);
1527
- return {
1528
- id: userId,
1529
- type: 'user',
1530
- };
1531
- }
1532
- }
1533
- formatMessage(message) {
1534
- return message;
1535
- }
1536
- async getUnreadCount(roomId) {
1537
- const { items } = await this.messageService.query({
1538
- skip: 0,
1539
- take: 99,
1540
- filter: { field: 'roomId', value: roomId, operator: { type: 'equal' } },
1541
- });
1542
- const currentUserId = this.getCurrentUser().id;
1543
- const unreadMessages = items.filter((message) => {
1544
- const isFromCurrentUser = message.author.id === currentUserId;
1545
- const isReadByCurrentUser = (message.seen || []).some((seen) => seen.author.id === currentUserId);
1546
- return !isFromCurrentUser && !isReadByCurrentUser;
1447
+ handleClose() {
1448
+ this.close({
1449
+ lookup: this.context()?.lookup,
1450
+ title: this.context()?.title,
1547
1451
  });
1548
- return unreadMessages.length;
1549
- }
1550
- //#endregion
1551
- //#region ---- ChatRoomService Implementations ----
1552
- async createRoom(memberIds, title) {
1553
- if (memberIds.length === 0) {
1554
- throw new Error('At least one member is required to create a room');
1555
- }
1556
- const currentUser = this.getCurrentUser();
1557
- const allMemberIds = [...new Set([...memberIds, currentUser.id])];
1558
- const memberReferences = await Promise.all(allMemberIds.map((id) => this.getUserInfo(id)));
1559
- const userNames = await Promise.all(memberReferences.map(async (ref) => {
1560
- const user = await this.usersService.getOne(ref.id);
1561
- return `${user.firstName} ${user.lastName}`.trim();
1562
- }));
1563
- const room = {
1564
- members: memberReferences,
1565
- title: title,
1566
- topic: memberIds.length > 2 ? 'group' : 'personal',
1567
- createdAt: new Date(),
1568
- updatedAt: new Date(),
1569
- createdBy: this.getCurrentUser().id,
1570
- updatedBy: this.getCurrentUser().id,
1571
- };
1572
- const roomId = await this.roomService.insertOne(room);
1573
- return this.getRoom(roomId);
1574
1452
  }
1575
- async getRoom(roomId) {
1576
- const room = await this.roomService.getOne(roomId);
1577
- if (!room) {
1578
- throw new Error(`Room with ID ${roomId} not found.`);
1453
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMUserLookupPopup, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1454
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: AXMUserLookupPopup, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `<axp-widgets-container [context]="context()" (onContextChanged)="context.set($event.data)">
1455
+ <div class="ax-p-4 ax-flex ax-flex-col ax-gap-4">
1456
+ <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
1457
+ @if (context()?.lookup?.length > 1) {
1458
+ <ng-container axp-widget-renderer [node]="titleNode" [mode]="'edit'"> </ng-container>
1579
1459
  }
1580
- const messages = await this.getMessages(roomId, 0, 1);
1581
- const lastMessage = messages.items.length > 0 ? messages.items[0] : undefined;
1582
- const unreadCount = await this.getUnreadCount(roomId);
1583
- const currentUserId = this.getCurrentUser().id;
1584
- return {
1585
- ...room,
1586
- lastMessage,
1587
- unreadCount,
1588
- members: room.members.filter((m) => m.id !== currentUserId),
1589
- };
1590
- }
1460
+ </div>
1461
+ </axp-widgets-container>
1462
+
1463
+ <ax-footer>
1464
+ <ax-suffix>
1465
+ <ax-button
1466
+ text="Accept & Send"
1467
+ color="primary"
1468
+ [disabled]="context()?.lookup?.length > 1 && !context()?.title"
1469
+ (onClick)="handleClose()"
1470
+ ></ax-button>
1471
+ </ax-suffix>
1472
+ </ax-footer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.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: i5.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }] }); }
1473
+ }
1474
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMUserLookupPopup, decorators: [{
1475
+ type: Component,
1476
+ args: [{
1477
+ template: `<axp-widgets-container [context]="context()" (onContextChanged)="context.set($event.data)">
1478
+ <div class="ax-p-4 ax-flex ax-flex-col ax-gap-4">
1479
+ <ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
1480
+ @if (context()?.lookup?.length > 1) {
1481
+ <ng-container axp-widget-renderer [node]="titleNode" [mode]="'edit'"> </ng-container>
1482
+ }
1483
+ </div>
1484
+ </axp-widgets-container>
1485
+
1486
+ <ax-footer>
1487
+ <ax-suffix>
1488
+ <ax-button
1489
+ text="Accept & Send"
1490
+ color="primary"
1491
+ [disabled]="context()?.lookup?.length > 1 && !context()?.title"
1492
+ (onClick)="handleClose()"
1493
+ ></ax-button>
1494
+ </ax-suffix>
1495
+ </ax-footer>`,
1496
+ imports: [AXDecoratorModule, AXPLayoutBuilderModule, AXButtonModule],
1497
+ }]
1498
+ }] });
1499
+
1500
+ class AXMCommentWidgetViewComponent extends AXPValueWidgetComponent {
1501
+ constructor() {
1502
+ super(...arguments);
1503
+ this.refrenceType = computed(() => this.options().refrenceType);
1504
+ this.refrenceId = computed(() => this.options().refrenceId);
1505
+ this.subject = computed(() => this.options().subject);
1506
+ }
1507
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1508
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.6", type: AXMCommentWidgetViewComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `
1509
+ <axm-comment [refrenceId]="refrenceId()" [refrenceType]="refrenceType()" [subject]="subject()"></axm-comment>
1510
+ `, isInline: true, dependencies: [{ kind: "component", type: AXMCommentComponent, selector: "axm-comment", inputs: ["refrenceType", "refrenceId", "subject"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1511
+ }
1512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentWidgetViewComponent, decorators: [{
1513
+ type: Component,
1514
+ args: [{
1515
+ template: `
1516
+ <axm-comment [refrenceId]="refrenceId()" [refrenceType]="refrenceType()" [subject]="subject()"></axm-comment>
1517
+ `,
1518
+ changeDetection: ChangeDetectionStrategy.OnPush,
1519
+ imports: [AXMCommentComponent],
1520
+ }]
1521
+ }] });
1522
+
1523
+ var commentWidgetView_component = /*#__PURE__*/Object.freeze({
1524
+ __proto__: null,
1525
+ AXMCommentWidgetViewComponent: AXMCommentWidgetViewComponent
1526
+ });
1527
+
1528
+ const AXPCommentWidget = {
1529
+ name: 'comment',
1530
+ title: 'Comments',
1531
+ description: 'Display and manage comments for entities',
1532
+ type: 'view',
1533
+ categories: [],
1534
+ groups: [AXPWidgetGroupEnum.FormElement],
1535
+ icon: 'fa-solid fa-comments',
1536
+ properties: [],
1537
+ components: {
1538
+ view: {
1539
+ component: () => Promise.resolve().then(function () { return commentWidgetView_component; }).then((c) => c.AXMCommentWidgetViewComponent),
1540
+ },
1541
+ },
1542
+ };
1543
+
1544
+ class AXMCommentPopupStartAction extends AXPWorkflowAction {
1545
+ constructor() {
1546
+ super(...arguments);
1547
+ this.popupService = inject(AXPopupService);
1548
+ this.translate = inject(AXTranslationService);
1549
+ }
1550
+ async execute(context) {
1551
+ const comp = (await Promise.resolve().then(function () { return commentPopup_component; })).AXMCommentPopupComponent;
1552
+ this.popupService.open(comp, {
1553
+ title: await this.translate.translateAsync('Comments'),
1554
+ size: 'lg',
1555
+ data: {
1556
+ refrenceType: signal(context.getVariable('options.refType')),
1557
+ refrenceId: signal(context.getVariable('options.refId')),
1558
+ subject: signal(context.getVariable('options.subject')),
1559
+ },
1560
+ });
1561
+ }
1562
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentPopupStartAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1563
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentPopupStartAction }); }
1564
+ }
1565
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMCommentPopupStartAction, decorators: [{
1566
+ type: Injectable
1567
+ }] });
1568
+ const AXMCommentPopupWorkflow = {
1569
+ startStepId: 'start',
1570
+ steps: {
1571
+ start: {
1572
+ action: 'show-comment-popup-action',
1573
+ },
1574
+ },
1575
+ };
1576
+
1577
+ //#region ---- Abstract Chat Service ----
1578
+ class AXMChatService {
1579
+ }
1580
+ //#endregion
1581
+ class AXMChatServiceImpl {
1582
+ constructor() {
1583
+ this.roomService = inject(AXMRoomService);
1584
+ this.messageService = inject(AXMMessageService);
1585
+ this.sessionService = inject(AXPSessionService);
1586
+ this.usersService = inject(AXMUsersEntityService);
1587
+ this._messageSent$ = new Subject();
1588
+ this.messageSent$ = this._messageSent$.asObservable();
1589
+ this._messageRemoved$ = new Subject();
1590
+ this.messageRemoved$ = this._messageRemoved$.asObservable();
1591
+ this._messageSeen$ = new Subject();
1592
+ this.messageSeen$ = this._messageSeen$.asObservable();
1593
+ this._roomAdded$ = new Subject();
1594
+ this.roomAdded$ = this._roomAdded$.asObservable();
1595
+ this._roomRemoved$ = new Subject();
1596
+ this.roomRemoved$ = this._roomRemoved$.asObservable();
1597
+ this._roomSeen$ = new Subject();
1598
+ this.roomSeen$ = this._roomSeen$.asObservable();
1599
+ this._messageReacted$ = new Subject();
1600
+ this.messageReacted$ = this._messageReacted$.asObservable();
1601
+ this._typingStatus$ = new Subject();
1602
+ this.typingStatus$ = this._typingStatus$.asObservable();
1603
+ }
1604
+ //#region ---- Helper Methods ----
1605
+ getCurrentUser() {
1606
+ const user = this.sessionService.user;
1607
+ return {
1608
+ id: user?.id || 'guest-user',
1609
+ type: 'user',
1610
+ fullName: user?.title?.trim() || 'Guest User',
1611
+ username: user?.name?.trim() || 'guest-user',
1612
+ };
1613
+ }
1614
+ async getUserInfo(userId) {
1615
+ try {
1616
+ const user = await this.usersService.getOne(userId);
1617
+ return {
1618
+ id: user.id,
1619
+ type: 'user',
1620
+ fullName: `${user.firstName} ${user.lastName}`.trim(),
1621
+ username: user.username?.trim(),
1622
+ };
1623
+ }
1624
+ catch (error) {
1625
+ console.error(`Failed to get user info for ID: ${userId}`, error);
1626
+ return {
1627
+ id: userId,
1628
+ type: 'user',
1629
+ fullName: 'Unknown User',
1630
+ username: 'Unknown User',
1631
+ };
1632
+ }
1633
+ }
1634
+ async formatMessage(message) {
1635
+ if (message.author && !message.author.fullName) {
1636
+ const authorInfo = await this.getUserInfo(message.author.id);
1637
+ return {
1638
+ ...message,
1639
+ author: authorInfo,
1640
+ };
1641
+ }
1642
+ return message;
1643
+ }
1644
+ async getUnreadCount(roomId) {
1645
+ const { items } = await this.messageService.query({
1646
+ skip: 0,
1647
+ take: 99,
1648
+ filter: { field: 'roomId', value: roomId, operator: { type: 'equal' } },
1649
+ });
1650
+ const currentUserId = this.getCurrentUser().id;
1651
+ const unreadMessages = items.filter((message) => {
1652
+ const isFromCurrentUser = message.author.id === currentUserId;
1653
+ const isReadByCurrentUser = (message.seen || []).some((seen) => seen.author.id === currentUserId);
1654
+ return !isFromCurrentUser && !isReadByCurrentUser;
1655
+ });
1656
+ return unreadMessages.length;
1657
+ }
1658
+ //#endregion
1659
+ //#region ---- ChatRoomService Implementations ----
1660
+ async createRoom(memberIds, title) {
1661
+ if (memberIds.length === 0) {
1662
+ throw new Error('At least one member is required to create a room');
1663
+ }
1664
+ const currentUser = this.getCurrentUser();
1665
+ const allMemberIds = [...new Set([...memberIds, currentUser.id])];
1666
+ const memberReferences = await Promise.all(allMemberIds.map((id) => this.getUserInfo(id)));
1667
+ const room = {
1668
+ members: memberReferences,
1669
+ title: title,
1670
+ topic: memberIds.length > 1 ? 'group' : 'personal',
1671
+ createdAt: new Date(),
1672
+ updatedAt: new Date(),
1673
+ type: 'chat',
1674
+ createdBy: this.getCurrentUser().id,
1675
+ updatedBy: this.getCurrentUser().id,
1676
+ };
1677
+ const roomId = await this.roomService.insertOne(room);
1678
+ const newRoom = await this.getRoom(roomId);
1679
+ this._roomAdded$.next(newRoom);
1680
+ return newRoom;
1681
+ }
1682
+ async getRoom(roomId) {
1683
+ const room = await this.roomService.getOne(roomId);
1684
+ if (!room || room.type !== 'chat') {
1685
+ throw new Error(`Chat room with ID ${roomId} not found.`);
1686
+ }
1687
+ const messages = await this.getMessages(roomId, 0, 1);
1688
+ const lastMessage = messages.items.length > 0 ? messages.items[0] : undefined;
1689
+ const unreadCount = await this.getUnreadCount(roomId);
1690
+ const currentUserId = this.getCurrentUser().id;
1691
+ const members = await Promise.all(room.members.map(async (member) => {
1692
+ if (member.fullName) {
1693
+ return member;
1694
+ }
1695
+ return this.getUserInfo(member.id);
1696
+ }));
1697
+ return {
1698
+ ...room,
1699
+ lastMessage,
1700
+ unreadCount,
1701
+ members: members.filter((m) => m.id !== currentUserId),
1702
+ };
1703
+ }
1591
1704
  async listRooms(skip = 0, take = 100) {
1592
- const { items, total } = await this.roomService.query({ skip, take });
1705
+ const { items, total } = await this.roomService.query({
1706
+ skip,
1707
+ take,
1708
+ filter: { field: 'type', value: 'chat', operator: { type: 'equal' } },
1709
+ });
1593
1710
  const settledRooms = await Promise.allSettled(items.map((room) => this.getRoom(room.id)));
1594
1711
  const chatRooms = settledRooms
1595
1712
  .filter((result) => {
@@ -1609,6 +1726,7 @@ class AXMChatServiceImpl {
1609
1726
  async deleteRoom(roomId) {
1610
1727
  try {
1611
1728
  await this.roomService.deleteOne(roomId);
1729
+ this._roomRemoved$.next(roomId);
1612
1730
  return true;
1613
1731
  }
1614
1732
  catch (error) {
@@ -1638,8 +1756,8 @@ class AXMChatServiceImpl {
1638
1756
  }
1639
1757
  //#endregion
1640
1758
  //#region ---- ChatMessageService Implementations ----
1641
- async sendMessage(roomId, content, contentType = 'text', replyId) {
1642
- const author = this.getCurrentUser();
1759
+ async sendMessage(roomId, content, contentType = 'text', replyId, userId) {
1760
+ const author = userId ? await this.getUserInfo(userId) : this.getCurrentUser();
1643
1761
  const messageContent = { content, contentType };
1644
1762
  const message = {
1645
1763
  roomId,
@@ -1655,7 +1773,7 @@ class AXMChatServiceImpl {
1655
1773
  };
1656
1774
  const messageId = await this.messageService.insertOne({ ...message });
1657
1775
  const newMessage = await this.messageService.getOne(messageId);
1658
- const formattedMessage = this.formatMessage(newMessage);
1776
+ const formattedMessage = await this.formatMessage(newMessage);
1659
1777
  this._messageSent$.next(formattedMessage);
1660
1778
  return formattedMessage;
1661
1779
  }
@@ -1666,7 +1784,7 @@ class AXMChatServiceImpl {
1666
1784
  filter: { field: 'roomId', value: roomId, operator: { type: 'equal' } },
1667
1785
  sort: [{ field: 'createdAt', dir: 'desc' }],
1668
1786
  });
1669
- const formattedMessages = result.items.map((m) => this.formatMessage(m));
1787
+ const formattedMessages = await Promise.all(result.items.map((m) => this.formatMessage(m)));
1670
1788
  return { items: formattedMessages, total: result.total };
1671
1789
  }
1672
1790
  async getMessage(messageId) {
@@ -1684,6 +1802,7 @@ class AXMChatServiceImpl {
1684
1802
  async deleteMessage(messageId) {
1685
1803
  try {
1686
1804
  await this.messageService.deleteOne(messageId);
1805
+ this._messageRemoved$.next(messageId);
1687
1806
  return true;
1688
1807
  }
1689
1808
  catch (error) {
@@ -1718,21 +1837,25 @@ class AXMChatServiceImpl {
1718
1837
  return message;
1719
1838
  }
1720
1839
  const updatedReactions = [...reactions, { author, type }];
1721
- return this.messageService.updateOne(messageId, {
1840
+ const res = await this.messageService.updateOne(messageId, {
1722
1841
  reactions: updatedReactions,
1723
1842
  updatedAt: new Date(),
1724
1843
  updatedBy: this.getCurrentUser().id,
1725
1844
  });
1845
+ this._messageReacted$.next(res);
1846
+ return res;
1726
1847
  }
1727
1848
  async removeReaction(messageId, type) {
1728
1849
  const author = this.getCurrentUser();
1729
1850
  const message = await this.messageService.getOne(messageId);
1730
1851
  const updatedReactions = (message.reactions || []).filter((r) => !(r.author.id === author.id && r.type === type));
1731
- return this.messageService.updateOne(messageId, {
1852
+ const res = await this.messageService.updateOne(messageId, {
1732
1853
  reactions: updatedReactions,
1733
1854
  updatedAt: new Date(),
1734
1855
  updatedBy: this.getCurrentUser().id,
1735
1856
  });
1857
+ this._messageReacted$.next(res);
1858
+ return res;
1736
1859
  }
1737
1860
  async getReactions(messageId) {
1738
1861
  const message = await this.messageService.getOne(messageId);
@@ -1748,11 +1871,13 @@ class AXMChatServiceImpl {
1748
1871
  return message;
1749
1872
  }
1750
1873
  const updatedSeen = [...seen, { author, type: 'read' }];
1751
- return this.messageService.updateOne(messageId, {
1874
+ const updatedMessage = await this.messageService.updateOne(messageId, {
1752
1875
  seen: updatedSeen,
1753
1876
  updatedAt: new Date(),
1754
1877
  updatedBy: this.getCurrentUser().id,
1755
1878
  });
1879
+ this._messageSeen$.next(await this.formatMessage(updatedMessage));
1880
+ return updatedMessage;
1756
1881
  }
1757
1882
  async getSeenBy(messageId) {
1758
1883
  const message = await this.messageService.getOne(messageId);
@@ -1769,6 +1894,7 @@ class AXMChatServiceImpl {
1769
1894
  if (unreadMessages.length > 0) {
1770
1895
  await Promise.all(unreadMessages.map((message) => this.markSeen(message.id)));
1771
1896
  }
1897
+ this._roomSeen$.next(roomId);
1772
1898
  return true;
1773
1899
  }
1774
1900
  catch (error) {
@@ -1776,10 +1902,20 @@ class AXMChatServiceImpl {
1776
1902
  return false;
1777
1903
  }
1778
1904
  }
1779
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1780
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, providedIn: 'root' }); }
1905
+ //#endregion
1906
+ //#region ---- Typing Status Implementations ----
1907
+ async startTyping(roomId, userId) {
1908
+ const user = userId ? await this.getUserInfo(userId) : this.getCurrentUser();
1909
+ this._typingStatus$.next({ roomId, user, isTyping: true });
1910
+ }
1911
+ async stopTyping(roomId, userId) {
1912
+ const user = userId ? await this.getUserInfo(userId) : this.getCurrentUser();
1913
+ this._typingStatus$.next({ roomId, user, isTyping: false });
1914
+ }
1915
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1916
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatServiceImpl, providedIn: 'root' }); }
1781
1917
  }
1782
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, decorators: [{
1918
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatServiceImpl, decorators: [{
1783
1919
  type: Injectable,
1784
1920
  args: [{
1785
1921
  providedIn: 'root',
@@ -1790,31 +1926,37 @@ class AXMChatItemComponent {
1790
1926
  constructor() {
1791
1927
  // Input
1792
1928
  this.data = input.required();
1929
+ this.typing = input(false);
1930
+ this.lastMessageReaction = input(null);
1793
1931
  // Output
1794
1932
  this.pressChatItem = output();
1795
1933
  // Services
1796
1934
  this.sessionService = inject(AXPSessionService);
1797
- this.userService = inject(AXMUsersEntityService);
1798
1935
  this.datePipe = inject(DatePipe);
1799
- this.chatName = signal({ fullName: 'Loading...', id: '' });
1936
+ this.chatName = signal({
1937
+ fullName: 'Loading...',
1938
+ id: '',
1939
+ isPrivate: false,
1940
+ });
1800
1941
  this.#chatInfoEffect = effect(async () => {
1801
1942
  const { members, title, id } = this.data();
1802
1943
  if (members.length > 1) {
1803
1944
  this.chatName.set({
1804
1945
  fullName: title || 'Group Chat',
1805
1946
  id,
1947
+ isPrivate: false,
1806
1948
  });
1807
1949
  return;
1808
1950
  }
1809
1951
  const member = members[0];
1810
1952
  if (!member) {
1811
- this.chatName.set({ fullName: 'Unknown User', id: '' });
1953
+ this.chatName.set({ fullName: 'Unknown User', id: '', isPrivate: true });
1812
1954
  return;
1813
1955
  }
1814
- const data = await this.userService.getOne(member.id);
1815
1956
  this.chatName.set({
1816
- fullName: `${data.firstName} ${data.lastName}`,
1957
+ fullName: member.fullName || 'Unknown User',
1817
1958
  id: member.id,
1959
+ isPrivate: true,
1818
1960
  });
1819
1961
  });
1820
1962
  this.formattedLastMessageDate = computed(() => {
@@ -1835,12 +1977,14 @@ class AXMChatItemComponent {
1835
1977
  }
1836
1978
  });
1837
1979
  this.lastMessage = computed(() => {
1838
- const message = this.data().lastMessage?.message;
1980
+ const message = this.data().lastMessage;
1839
1981
  if (!message)
1840
- return { content: 'No messages yet', contentType: 'text' };
1982
+ return { content: 'No messages yet', contentType: 'text', reactions: [] };
1841
1983
  return {
1842
- content: message.contentType === 'text' ? message.content : message.contentType,
1843
- contentType: message.contentType,
1984
+ content: message.message.contentType === 'text' ? message.message.content : message.message.contentType,
1985
+ contentType: message.message.contentType,
1986
+ reactions: message.reactions || [],
1987
+ author: message.author,
1844
1988
  };
1845
1989
  });
1846
1990
  this.hasUnread = computed(() => {
@@ -1865,12 +2009,12 @@ class AXMChatItemComponent {
1865
2009
  }
1866
2010
  return '';
1867
2011
  }
1868
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1869
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMChatItemComponent, isStandalone: true, 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-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 <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-2\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-pt-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n <!-- @default {\n <i class=\"fa-regular fa-message\"></i>\n } -->\n }\n <p class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </p>\n </div>\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: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2012
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2013
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: AXMChatItemComponent, isStandalone: true, selector: "axm-chat-item", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, typing: { classPropertyName: "typing", publicName: "typing", isSignal: true, isRequired: false, transformFunction: null }, lastMessageReaction: { classPropertyName: "lastMessageReaction", publicName: "lastMessageReaction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pressChatItem: "pressChatItem" }, ngImport: i0, template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n @if (chatName().isPrivate) {\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n } @else {\n <ax-avatar [size]=\"48\">\n <ax-text class=\"ax-primary-lightest\">\n <small class=\"ax-text-xs ax-font-semibold fas fa-users\"></small>\n </ax-text>\n </ax-avatar>\n }\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-1\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-py-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n @if (typing()) {\n <div class=\"ax-pb-1 ax-flex ax-justify-center ax-items-end ax-gap-1\">\n <span>is typing</span>\n <div class=\"typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n </div>\n } @else {\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @if ((lastMessage().author?.fullName || lastMessage().author?.username) && !chatName().isPrivate) {\n <span class=\"ax-text-on-surface-light ax-font-semibold ax-capitalize\">\n {{ lastMessage().author?.fullName || lastMessage().author?.username + ': ' }}\n </span>\n }\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n }\n\n <span class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </span>\n </div>\n }\n <div class=\"ax-flex ax-items-center ax-gap-2\">\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n <!-- @if (lastMessageReaction()) {\n <div class=\"ax-flex ax-items-center ax-gap-1 ax-bg-surface ax-rounded-full ax-p-1 ax-px-2\">\n <span class=\"ax-text-sm\">{{ lastMessageReaction() }}</span>\n <span class=\"ax-text-xs\">{{ lastMessage().reactions.length }}</span>\n </div>\n } -->\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.typing-indicator{display:flex;align-items:center;padding-bottom:.1rem}.typing-indicator span{height:6px;width:6px;margin:0 1px;background-color:#9e9ea1;border-radius:50%;display:inline-block;animation:bounce 1.4s infinite ease-in-out both}.typing-indicator span:nth-child(1){animation-delay:-.32s}.typing-indicator span:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i3.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1870
2014
  }
1871
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatItemComponent, decorators: [{
2015
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatItemComponent, decorators: [{
1872
2016
  type: Component,
1873
- args: [{ selector: 'axm-chat-item', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXDecoratorModule, AXBadgeModule, AXPUserAvatarComponent], template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-2\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-pt-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n <!-- @default {\n <i class=\"fa-regular fa-message\"></i>\n } -->\n }\n <p class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </p>\n </div>\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: [":host{display:block;width:100%}\n"] }]
2017
+ args: [{ selector: 'axm-chat-item', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXDecoratorModule, AXBadgeModule, AXPUserAvatarComponent, AXAvatarModule, AXDecoratorModule], template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n @if (chatName().isPrivate) {\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n } @else {\n <ax-avatar [size]=\"48\">\n <ax-text class=\"ax-primary-lightest\">\n <small class=\"ax-text-xs ax-font-semibold fas fa-users\"></small>\n </ax-text>\n </ax-avatar>\n }\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-1\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-py-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n @if (typing()) {\n <div class=\"ax-pb-1 ax-flex ax-justify-center ax-items-end ax-gap-1\">\n <span>is typing</span>\n <div class=\"typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n </div>\n } @else {\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @if ((lastMessage().author?.fullName || lastMessage().author?.username) && !chatName().isPrivate) {\n <span class=\"ax-text-on-surface-light ax-font-semibold ax-capitalize\">\n {{ lastMessage().author?.fullName || lastMessage().author?.username + ': ' }}\n </span>\n }\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n }\n\n <span class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </span>\n </div>\n }\n <div class=\"ax-flex ax-items-center ax-gap-2\">\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n <!-- @if (lastMessageReaction()) {\n <div class=\"ax-flex ax-items-center ax-gap-1 ax-bg-surface ax-rounded-full ax-p-1 ax-px-2\">\n <span class=\"ax-text-sm\">{{ lastMessageReaction() }}</span>\n <span class=\"ax-text-xs\">{{ lastMessage().reactions.length }}</span>\n </div>\n } -->\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.typing-indicator{display:flex;align-items:center;padding-bottom:.1rem}.typing-indicator span{height:6px;width:6px;margin:0 1px;background-color:#9e9ea1;border-radius:50%;display:inline-block;animation:bounce 1.4s infinite ease-in-out both}.typing-indicator span:nth-child(1){animation-delay:-.32s}.typing-indicator span:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}\n"] }]
1874
2018
  }] });
1875
2019
 
1876
2020
  class AXMChatComponent extends AXPPageLayoutBaseComponent {
@@ -1878,8 +2022,8 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
1878
2022
  super(...arguments);
1879
2023
  this.activatedRoute = inject(ActivatedRoute);
1880
2024
  this.chatService = inject(AXMChatService);
1881
- this.userService = inject(AXMUsersEntityService);
1882
2025
  this.popupService = inject(AXPopupService);
2026
+ this.dialogService = inject(AXDialogService);
1883
2027
  this.router = inject(Router);
1884
2028
  this.unsubscribe = inject(AXUnsubscriber);
1885
2029
  // View Children
@@ -1890,6 +2034,7 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
1890
2034
  this.allRooms = signal([]);
1891
2035
  this.selectedRoom = signal(null);
1892
2036
  this.isLoading = signal(false);
2037
+ this.typingStatus = signal({});
1893
2038
  this.error = signal(null);
1894
2039
  this.activeTab = signal(0);
1895
2040
  this.isSearching = signal(false);
@@ -1931,10 +2076,9 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
1931
2076
  roomName = room.title || '';
1932
2077
  }
1933
2078
  else if (room.members && room.members.length === 1) {
1934
- const memberId = room.members[0].id;
1935
- if (memberId) {
1936
- const user = await this.userService.getOne(memberId);
1937
- roomName = `${user.firstName} ${user.lastName}` || '';
2079
+ const member = room.members[0];
2080
+ if (member) {
2081
+ roomName = member.fullName || '';
1938
2082
  }
1939
2083
  }
1940
2084
  const lastMessage = room.lastMessage?.message?.content || '';
@@ -1958,6 +2102,12 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
1958
2102
  this.allCount = computed(() => this.allRooms().length);
1959
2103
  this.hasUnread = computed(() => this.allRooms().filter((i) => i.unreadCount > 0).length > 0);
1960
2104
  this.totalCount = computed(() => this.allRooms().length || 0);
2105
+ //switch tab to unread if there is no unread
2106
+ this.autoSwitchTab = effect(() => {
2107
+ if (this.activeTab() === 1 && !this.hasUnread()) {
2108
+ this.activeTab.set(0);
2109
+ }
2110
+ });
1961
2111
  this.af = afterNextRender(() => {
1962
2112
  const tabComponent = this.tab();
1963
2113
  if (tabComponent) {
@@ -1984,14 +2134,96 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
1984
2134
  await this.loadFromRoute();
1985
2135
  });
1986
2136
  //
1987
- this.messageSentSubscription = this.chatService.messageSent$.subscribe(() => {
1988
- this.refreshChat();
2137
+ this.chatService.roomRemoved$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((roomId) => {
2138
+ this.allRooms.update((rooms) => rooms.filter((r) => r.id !== roomId));
2139
+ if (roomId === this.selectedRoom()?.id) {
2140
+ this.selectedRoom.set(null);
2141
+ this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2142
+ }
2143
+ });
2144
+ this.chatService.roomSeen$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((roomId) => {
2145
+ const currentRooms = this.allRooms();
2146
+ const updatedRooms = currentRooms.map((room) => {
2147
+ if (room.id === roomId) {
2148
+ return { ...room, unreadCount: 0 };
2149
+ }
2150
+ return room;
2151
+ });
2152
+ this.allRooms.set(updatedRooms);
2153
+ });
2154
+ this.chatService.messageSeen$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((message) => {
2155
+ const currentRooms = this.allRooms();
2156
+ const updatedRooms = currentRooms.map((room) => {
2157
+ if (room.id === message.roomId) {
2158
+ return { ...room, unreadCount: room.unreadCount - 1 };
2159
+ }
2160
+ return room;
2161
+ });
2162
+ this.allRooms.set(updatedRooms);
2163
+ });
2164
+ this.chatService.typingStatus$.pipe(this.unsubscribe.takeUntilDestroy).subscribe(async (status) => {
2165
+ const currentUserId = this.sessionService.user?.id;
2166
+ if (status.user.id !== currentUserId) {
2167
+ if (status.isTyping) {
2168
+ this.typingStatus.update((value) => ({
2169
+ ...value,
2170
+ [status.roomId]: { user: status.user.fullName || '', isTyping: true },
2171
+ }));
2172
+ }
2173
+ else {
2174
+ this.typingStatus.update((value) => {
2175
+ const { [status.roomId]: _, ...rest } = value;
2176
+ return rest;
2177
+ });
2178
+ }
2179
+ }
2180
+ });
2181
+ this.chatService.messageSent$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((message) => {
2182
+ const rooms = this.allRooms();
2183
+ const roomIndex = rooms.findIndex((r) => r.id === message.roomId);
2184
+ if (roomIndex > -1) {
2185
+ const roomToUpdate = rooms[roomIndex];
2186
+ const updatedRoom = {
2187
+ ...roomToUpdate,
2188
+ lastMessage: message,
2189
+ unreadCount: message.author.id !== this.sessionService.user?.id
2190
+ ? (roomToUpdate.unreadCount || 0) + 1
2191
+ : roomToUpdate.unreadCount,
2192
+ };
2193
+ const newRooms = [updatedRoom, ...rooms.filter((r) => r.id !== message.roomId)];
2194
+ this.allRooms.set(newRooms);
2195
+ }
2196
+ else {
2197
+ // New room, fetch it and add it to the top.
2198
+ this.chatService.getRoom(message.roomId).then((newRoom) => {
2199
+ this.allRooms.update((currentRooms) => {
2200
+ // Prepending the new room
2201
+ return [newRoom, ...currentRooms];
2202
+ });
2203
+ });
2204
+ }
2205
+ });
2206
+ this.chatService.messageReacted$.pipe(this.unsubscribe.takeUntilDestroy).subscribe((message) => {
2207
+ this.allRooms.update((rooms) => {
2208
+ return rooms.map((room) => {
2209
+ if (room.lastMessage?.id === message.id) {
2210
+ return {
2211
+ ...room,
2212
+ lastMessage: {
2213
+ ...room.lastMessage,
2214
+ reactions: message.reactions,
2215
+ },
2216
+ };
2217
+ }
2218
+ return room;
2219
+ });
2220
+ });
1989
2221
  });
1990
2222
  }
1991
2223
  async loadFromRoute() {
1992
2224
  const chatId = this.activatedRoute.snapshot.firstChild?.paramMap.get('id');
1993
2225
  if (chatId) {
1994
- this.markChatAsRead(chatId);
2226
+ this.openChat(chatId);
1995
2227
  }
1996
2228
  else {
1997
2229
  this.selectedRoom.set(null);
@@ -2000,6 +2232,9 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
2000
2232
  // Page Interface Implementation
2001
2233
  async getPageTitle() {
2002
2234
  const room = this.selectedRoom();
2235
+ if (!room && this.layoutService.isMobileDevice()) {
2236
+ return await this.translateService.translateAsync('plural-title', { scope: 'conversation' });
2237
+ }
2003
2238
  if (!room) {
2004
2239
  return '';
2005
2240
  }
@@ -2011,10 +2246,14 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
2011
2246
  if (!member) {
2012
2247
  return 'Unknown User';
2013
2248
  }
2014
- const user = await this.userService.getOne(member.id);
2015
- return `${user.firstName} ${user.lastName}` || 'Unknown User';
2249
+ return member.fullName || 'Unknown User';
2016
2250
  }
2017
2251
  getPageDescription() {
2252
+ const typing = this.typingStatus();
2253
+ const selectedRoomId = this.selectedRoom()?.id;
2254
+ if (selectedRoomId && typing[selectedRoomId]?.isTyping) {
2255
+ return `${typing[selectedRoomId].user} is typing...`;
2256
+ }
2018
2257
  const room = this.selectedRoom();
2019
2258
  return room?.topic || '';
2020
2259
  }
@@ -2027,10 +2266,44 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
2027
2266
  else
2028
2267
  return null;
2029
2268
  }
2269
+ async getSecondaryMenuItems() {
2270
+ return this.layoutService.isMobileDevice()
2271
+ ? []
2272
+ : [
2273
+ {
2274
+ title: await this.translateService.translateAsync('remove-chat', { scope: 'conversation' }),
2275
+ icon: 'fa-light fa-trash-can',
2276
+ color: 'danger',
2277
+ command: {
2278
+ name: 'remove-chat',
2279
+ },
2280
+ },
2281
+ ];
2282
+ }
2283
+ async execute(command) {
2284
+ switch (command.name) {
2285
+ case 'remove-chat':
2286
+ const result = await this.dialogService.confirm('Remove Chat', 'Are you sure you want to remove this chat?');
2287
+ if (result) {
2288
+ await this.chatService.deleteRoom(this.selectedRoom()?.id);
2289
+ this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2290
+ }
2291
+ break;
2292
+ }
2293
+ }
2030
2294
  onBackButtonClick() {
2031
2295
  this.selectedRoom.set(null);
2296
+ this.typingStatus.update((value) => {
2297
+ const { [this.selectedRoom()?.id]: _, ...rest } = value;
2298
+ return rest;
2299
+ });
2032
2300
  this.router.navigate(['./'], { relativeTo: this.activatedRoute });
2033
2301
  }
2302
+ onKeydownHandler(event) {
2303
+ if (this.selectedRoom()) {
2304
+ this.onBackButtonClick();
2305
+ }
2306
+ }
2034
2307
  // Methods
2035
2308
  async loadChats() {
2036
2309
  try {
@@ -2053,8 +2326,12 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
2053
2326
  async refreshChat() {
2054
2327
  await this.loadChats();
2055
2328
  }
2056
- async markChatAsRead(chatId) {
2329
+ async openChat(chatId) {
2057
2330
  try {
2331
+ this.typingStatus.update((value) => {
2332
+ const { [chatId]: _, ...rest } = value;
2333
+ return rest;
2334
+ });
2058
2335
  // Use the service method to mark chat as read
2059
2336
  await this.chatService.markRoomSeen(chatId);
2060
2337
  // Update local state without making another network request
@@ -2101,7 +2378,7 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
2101
2378
  closeButton: true,
2102
2379
  closeOnBackdropClick: false,
2103
2380
  };
2104
- const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
2381
+ const popup = await this.popupService.open(AXMUserLookupPopup, popupConfig);
2105
2382
  const members = popup.data?.lookup;
2106
2383
  try {
2107
2384
  if (members) {
@@ -2114,25 +2391,31 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
2114
2391
  this.toastService.danger('Failed to create room');
2115
2392
  }
2116
2393
  }
2117
- ngOnDestroy() {
2118
- this.messageSentSubscription?.unsubscribe();
2119
- }
2120
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2121
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", providers: [
2394
+ ngOnDestroy() { }
2395
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2396
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", host: { listeners: { "document:keydown.escape": "onKeydownHandler($event)" } }, providers: [
2122
2397
  {
2123
2398
  provide: AXPPageLayoutBase,
2124
2399
  useExisting: AXMChatComponent,
2125
2400
  },
2126
2401
  AXUnsubscriber,
2127
- ], viewQueries: [{ propertyName: "tab", first: true, predicate: ["tab"], descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout #container>\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>{{ 'module-name' | translate: { scope: 'conversation' } | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0\">\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content style=\"height: calc(100vh - 22rem)\">\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<ng-template #template>\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n @if (hasUnread()) {\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"onTabChange($event)\"\n >\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 <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 </ax-tabs>\n }\n </div>\n\n <!-- Chat List Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between\" [class.ax-border-t]=\"hasUnread()\">\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-h-full ax-p-4 ax-text-center\">\n <ax-icon class=\"ax-text-danger ax-text-5xl ax-mb-3\">\n <i class=\"fa-light fa-circle-exclamation\"></i>\n </ax-icon>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-error' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-try-again' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-text-secondary ax-mb-4\">{{ error() }}</p>\n <ax-button\n [text]=\"'try-again' | translate: { scope: 'conversation' } | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div class=\"ax-flex-1 ax-overflow-y-auto\">\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n [attr.data-id]=\"i.id\"\n (click)=\"markChatAsRead(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\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</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: AXMChatItemComponent, selector: "axm-chat-item", inputs: ["data"], outputs: ["pressChatItem"] }, { kind: "ngmodule", type:
2402
+ ], viewQueries: [{ propertyName: "tab", first: true, predicate: ["tab"], descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout #container>\n <axp-layout-start-side class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>{{ 'module-name' | translate: { scope: 'conversation' } | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0 ax-max-w-80\">\n @if (!layoutService.isMobileDevice()) {\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content style=\"height: calc(100vh - 22rem)\">\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<ng-template #template>\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n @if (hasUnread()) {\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"onTabChange($event)\"\n >\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 <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 </ax-tabs>\n }\n </div>\n\n <!-- Chat List Content -->\n <div\n class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between ax-min-w-80\"\n [class.ax-border-t]=\"hasUnread()\"\n >\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-h-full ax-p-4 ax-text-center\">\n <ax-icon class=\"ax-text-danger ax-text-5xl ax-mb-3\">\n <i class=\"fa-light fa-circle-exclamation\"></i>\n </ax-icon>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-error' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-try-again' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-text-secondary ax-mb-4\">{{ error() }}</p>\n <ax-button\n [text]=\"'try-again' | translate: { scope: 'conversation' } | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div class=\"ax-flex-1 ax-overflow-y-auto\">\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n [typing]=\"$any(typingStatus()[i.id])?.isTyping || false\"\n [lastMessageReaction]=\"i.lastMessage?.reactions?.[0]?.type ?? null\"\n [attr.data-id]=\"i.id\"\n (click)=\"openChat(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\n [@fadeIn]\n tabindex=\"0\"\n (keydown.enter)=\"openChat(i.id)\"\n role=\"button\"\n ></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</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: AXMChatItemComponent, selector: "axm-chat-item", inputs: ["data", "typing", "lastMessageReaction"], outputs: ["pressChatItem"] }, { kind: "ngmodule", type:
2128
2403
  // Common Modules
2129
- CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$3.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type:
2404
+ CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$3.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type:
2130
2405
  // Acorex Core Modules
2131
- AXFormatModule }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXCommentModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }, { kind: "directive", type:
2406
+ AXFormatModule }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXCommentModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }, { kind: "ngmodule", type:
2132
2407
  // Acorex Component Modules
2133
- AXResizableDirective, selector: "[axResizable]", inputs: ["axResizable", "minWidth", "maxWidth", "dblClickAction", "width", "defaultWidth"], outputs: ["axResizableChange", "minWidthChange", "maxWidthChange", "dblClickActionChange", "widthChange", "defaultWidthChange", "onResizingStarted", "onResizingEnded", "onResizingDblClick"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i6$1.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: "ngmodule", type: AXTabsModule }, { kind: "component", type: i7$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i7$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2408
+ // AXResizableDirective,
2409
+ AXImageModule }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1$1.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i1$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i6$1.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: "ngmodule", type: AXTabsModule }, { kind: "component", type: i7$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i7$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }], animations: [
2410
+ trigger('fadeIn', [
2411
+ transition(':enter', [
2412
+ style({ opacity: 0, transform: 'translateY(10px)' }),
2413
+ animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
2414
+ ]),
2415
+ ]),
2416
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2134
2417
  }
2135
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatComponent, decorators: [{
2418
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatComponent, decorators: [{
2136
2419
  type: Component,
2137
2420
  args: [{ selector: 'axm-chat', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2138
2421
  {
@@ -2152,7 +2435,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
2152
2435
  AXCommentModule,
2153
2436
  AXTranslationModule,
2154
2437
  // Acorex Component Modules
2155
- AXResizableDirective,
2438
+ // AXResizableDirective,
2156
2439
  AXImageModule,
2157
2440
  AXAvatarModule,
2158
2441
  AXBadgeModule,
@@ -2175,8 +2458,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
2175
2458
  AXPThemeLayoutStartSideComponent,
2176
2459
  AXPThemeLayoutHeaderComponent,
2177
2460
  AXPThemeLayoutToolbarComponent,
2178
- ], template: "<axp-page-layout #container>\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>{{ 'module-name' | translate: { scope: 'conversation' } | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0\">\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content style=\"height: calc(100vh - 22rem)\">\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<ng-template #template>\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n @if (hasUnread()) {\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"onTabChange($event)\"\n >\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 <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 </ax-tabs>\n }\n </div>\n\n <!-- Chat List Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between\" [class.ax-border-t]=\"hasUnread()\">\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-h-full ax-p-4 ax-text-center\">\n <ax-icon class=\"ax-text-danger ax-text-5xl ax-mb-3\">\n <i class=\"fa-light fa-circle-exclamation\"></i>\n </ax-icon>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-error' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-try-again' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-text-secondary ax-mb-4\">{{ error() }}</p>\n <ax-button\n [text]=\"'try-again' | translate: { scope: 'conversation' } | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div class=\"ax-flex-1 ax-overflow-y-auto\">\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n [attr.data-id]=\"i.id\"\n (click)=\"markChatAsRead(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\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</ng-template>\n" }]
2179
- }] });
2461
+ ], animations: [
2462
+ trigger('fadeIn', [
2463
+ transition(':enter', [
2464
+ style({ opacity: 0, transform: 'translateY(10px)' }),
2465
+ animate('300ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
2466
+ ]),
2467
+ ]),
2468
+ ], template: "<axp-page-layout #container>\n <axp-layout-start-side class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>{{ 'module-name' | translate: { scope: 'conversation' } | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0 ax-max-w-80\">\n @if (!layoutService.isMobileDevice()) {\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content style=\"height: calc(100vh - 22rem)\">\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<ng-template #template>\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n @if (hasUnread()) {\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"onTabChange($event)\"\n >\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 <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 </ax-tabs>\n }\n </div>\n\n <!-- Chat List Content -->\n <div\n class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between ax-min-w-80\"\n [class.ax-border-t]=\"hasUnread()\"\n >\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-h-full ax-p-4 ax-text-center\">\n <ax-icon class=\"ax-text-danger ax-text-5xl ax-mb-3\">\n <i class=\"fa-light fa-circle-exclamation\"></i>\n </ax-icon>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-error' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-try-again' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-text-secondary ax-mb-4\">{{ error() }}</p>\n <ax-button\n [text]=\"'try-again' | translate: { scope: 'conversation' } | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div class=\"ax-flex-1 ax-overflow-y-auto\">\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n [typing]=\"$any(typingStatus()[i.id])?.isTyping || false\"\n [lastMessageReaction]=\"i.lastMessage?.reactions?.[0]?.type ?? null\"\n [attr.data-id]=\"i.id\"\n (click)=\"openChat(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\n [@fadeIn]\n tabindex=\"0\"\n (keydown.enter)=\"openChat(i.id)\"\n role=\"button\"\n ></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</ng-template>\n" }]
2469
+ }], propDecorators: { onKeydownHandler: [{
2470
+ type: HostListener,
2471
+ args: ['document:keydown.escape', ['$event']]
2472
+ }] } });
2180
2473
 
2181
2474
  var chat_component = /*#__PURE__*/Object.freeze({
2182
2475
  __proto__: null,
@@ -2188,7 +2481,7 @@ var chat_component = /*#__PURE__*/Object.freeze({
2188
2481
  * @param chatResponse - The response from chat service containing items and total
2189
2482
  * @returns Array of formatted chat preview messages
2190
2483
  */
2191
- function convertToChatPreview(currentUserId, chatResponse) {
2484
+ function convertToChatPreview(currentUserId, chatResponse, userName) {
2192
2485
  if (!chatResponse || !chatResponse.items?.length) {
2193
2486
  return [];
2194
2487
  }
@@ -2235,11 +2528,15 @@ function convertToChatPreview(currentUserId, chatResponse) {
2235
2528
  type: messageType,
2236
2529
  content: message.message?.content || '',
2237
2530
  name: name,
2531
+ userName,
2238
2532
  };
2239
2533
  // Add fromId if the message is not from current user
2240
2534
  if (currentUserId !== message.author.id) {
2241
2535
  previewMessage.fromId = message.author?.id;
2242
2536
  }
2537
+ else {
2538
+ previewMessage.showActionButton = true;
2539
+ }
2243
2540
  // Note: Reply information would need to be looked up from the actual messages
2244
2541
  // This would typically be done by the service that has access to all messages
2245
2542
  return previewMessage;
@@ -2250,7 +2547,7 @@ function convertToChatPreview(currentUserId, chatResponse) {
2250
2547
  * @param message - The original message that is being referenced
2251
2548
  * @returns A ChatPreviewMessage formatted for use as a replyTo
2252
2549
  */
2253
- function createReplyPreview(currentUserId, message) {
2550
+ function createReplyPreview(currentUserId, message, userName) {
2254
2551
  if (!message)
2255
2552
  return undefined;
2256
2553
  return {
@@ -2267,6 +2564,8 @@ function createReplyPreview(currentUserId, message) {
2267
2564
  content: message.message?.content || '',
2268
2565
  name: message.author?.id || 'Unknown',
2269
2566
  fromId: currentUserId !== message.author.id ? message.author?.id : undefined,
2567
+ showActionButton: true,
2568
+ userName,
2270
2569
  };
2271
2570
  }
2272
2571
  /**
@@ -2458,12 +2757,17 @@ class AXMChatPreviewComponent {
2458
2757
  this.chatService = inject(AXMChatService);
2459
2758
  this.sessionService = inject(AXPSessionService);
2460
2759
  this.toastService = inject(AXToastService);
2760
+ this.inputRef = viewChild(AXConversationInputComponent);
2761
+ this.activeMessage = signal(null);
2762
+ this.typing$ = new Subject();
2461
2763
  this.chatData = signal([]);
2764
+ this.room = signal(null);
2462
2765
  this.isEditing = signal(false);
2463
2766
  this.editId = signal(null);
2464
2767
  this.height = signal(0);
2465
2768
  this.isLoading = signal(false);
2466
2769
  this.error = signal(null);
2770
+ this.isChannel = computed(() => this.room()?.topic === 'channel');
2467
2771
  this.options = signal({
2468
2772
  disabled: false,
2469
2773
  readonly: false,
@@ -2477,11 +2781,38 @@ class AXMChatPreviewComponent {
2477
2781
  ngOnInit() {
2478
2782
  this.activatedRoute.params.subscribe((params) => {
2479
2783
  this.roomId = params['id'];
2480
- this.loadMessages(this.roomId);
2481
- // setTimeout(() => {
2482
- // this.scrollToEnd();
2483
- // }, 1000);
2784
+ if (this.roomId) {
2785
+ this.loadMessages(this.roomId);
2786
+ this.loadRoomInfo(this.roomId);
2787
+ }
2788
+ });
2789
+ this.chatService.messageSent$.subscribe(async (message) => {
2790
+ // Only add messages that are not from the current user and belong to the current room
2791
+ if (message.author.id !== this.sessionService.user?.id && message.roomId === this.roomId) {
2792
+ await this.chatService.markSeen(message.id);
2793
+ this.chatData.update((values) => {
2794
+ const newMessage = convertToChatPreview(this.sessionService.user?.id || '', {
2795
+ items: [message],
2796
+ total: 1,
2797
+ })[0];
2798
+ return [...values, newMessage];
2799
+ });
2800
+ }
2484
2801
  });
2802
+ // this.typingSubscription = this.typing$.pipe(debounceTime(1000)).subscribe(() => {
2803
+ // if (this.roomId) {
2804
+ // this.chatService.stopTyping(this.roomId);
2805
+ // }
2806
+ // });
2807
+ }
2808
+ ngOnDestroy() {
2809
+ this.typingSubscription?.unsubscribe();
2810
+ }
2811
+ handleTyping(event) {
2812
+ if (this.roomId) {
2813
+ this.chatService.startTyping(this.roomId);
2814
+ this.typing$.next();
2815
+ }
2485
2816
  }
2486
2817
  handleFileChange(event) {
2487
2818
  console.log('File Changed:', event);
@@ -2541,6 +2872,11 @@ class AXMChatPreviewComponent {
2541
2872
  }
2542
2873
  }
2543
2874
  }
2875
+ handleEnter() {
2876
+ if (this.options().value && this.roomId) {
2877
+ this.handleOnSend({ data: { value: this.options().value } });
2878
+ }
2879
+ }
2544
2880
  handleOnAction(e) {
2545
2881
  console.log('Action Triggered:', e);
2546
2882
  }
@@ -2574,10 +2910,20 @@ class AXMChatPreviewComponent {
2574
2910
  this.isLoading.set(false);
2575
2911
  }
2576
2912
  }
2577
- addItemHandler(e) {
2578
- if (e.data.fromId) {
2579
- e.canceled = true;
2580
- return;
2913
+ async loadRoomInfo(roomId) {
2914
+ try {
2915
+ const roomData = await this.chatService.getRoom(roomId);
2916
+ this.room.set(roomData);
2917
+ }
2918
+ catch (error) {
2919
+ console.error('Failed to load room info:', error);
2920
+ }
2921
+ }
2922
+ addItemHandler(e) {
2923
+ this.activeMessage.set(e.data);
2924
+ if (e.data.fromId) {
2925
+ e.canceled = true;
2926
+ return;
2581
2927
  }
2582
2928
  const userItem = [];
2583
2929
  if (e.data.type === 'text') {
@@ -2590,6 +2936,7 @@ class AXMChatPreviewComponent {
2590
2936
  this.isEditing.set(true);
2591
2937
  this.editId.set(e.data.id);
2592
2938
  this.options.update((prev) => ({ ...prev, value: e.data.content }));
2939
+ this.addInputOverlay('edit', e.data);
2593
2940
  }
2594
2941
  catch (error) {
2595
2942
  this.toastService.danger('Failed to edit message');
@@ -2619,479 +2966,26 @@ class AXMChatPreviewComponent {
2619
2966
  }
2620
2967
  e.items.push(...userItem);
2621
2968
  }
2622
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2623
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMChatPreviewComponent, isStandalone: true, selector: "axm-chat-preview", ngImport: i0, template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n [chatBoxHeight]=\"height() - 60 + 'px'\"\n ></ax-conversation-view>\n <ax-conversation-input\n class=\"ax-p-1\"\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 </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDomChangeDirective, selector: "[axDomChange]", outputs: ["axMutationObserver", "axResizeObserver"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "component", type: i2$4.AXConversationViewComponent, selector: "ax-conversation-view", inputs: ["chatBoxHeight", "isReplyArrowShown"], outputs: ["onScrollEnd", "onActionMenuOpening", "onAction"] }, { kind: "component", type: i2$4.AXConversationInputComponent, selector: "ax-conversation-input", inputs: ["look", "placeholder", "maxLength", "hasAttachment", "hasVoice", "hasEmoji", "isLoading", "acceptFileType"], outputs: ["onSendClick", "onStartRecording", "onCancelRecording", "onEnterPressed"] }, { kind: "component", type: i2$4.AXConversationContainerComponent, selector: "ax-conversation-container", inputs: ["chatData"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2624
- }
2625
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatPreviewComponent, decorators: [{
2626
- type: Component,
2627
- args: [{ selector: 'axm-chat-preview', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, AXDomChangeDirective, AXConversationModule, AXLoadingModule], template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n [chatBoxHeight]=\"height() - 60 + 'px'\"\n ></ax-conversation-view>\n <ax-conversation-input\n class=\"ax-p-1\"\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 </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
2628
- }] });
2629
-
2630
- var chatPreview_component = /*#__PURE__*/Object.freeze({
2631
- __proto__: null,
2632
- AXMChatPreviewComponent: AXMChatPreviewComponent
2633
- });
2634
-
2635
- class AXMCommentWidgetViewComponent extends AXPLayoutWidgetComponent {
2636
- constructor() {
2637
- super(...arguments);
2638
- this.hasCooldown = signal(false);
2639
- this.commentContent = signal('');
2640
- this.isSubmitting = signal(false);
2641
- this.isReplyingMode = signal(false);
2642
- this.isEditingMode = signal(false);
2643
- this.isLoading = signal(true);
2644
- this.failedImageIds = signal([]);
2645
- this.activeReplyComment = signal(undefined);
2646
- this.activeEditComment = signal(undefined);
2647
- this.wysiwygEditor = viewChild.required('w');
2648
- this.commentService = inject(AXMCommentService);
2649
- this.platform = inject(AXPlatform);
2650
- this.popupService = inject(AXPopupService);
2651
- this.toastService = inject(AXToastService);
2652
- this.dialogService = inject(AXDialogService);
2653
- this.sanitize = inject(DomSanitizer);
2654
- this.getPayload = computed(() => ({
2655
- params: this.payload(),
2656
- skip: 0,
2657
- take: 10,
2658
- }));
2659
- this.payload = computed(() => ({
2660
- roomType: 'default',
2661
- entityId: this.options().entityId,
2662
- instanceId: this.options().instanceId,
2663
- }));
2664
- this.comments = signal([]);
2665
- this.wysiwyg = viewChild('w');
2666
- this.wysiwygOptions = signal({
2667
- look: 'solid',
2668
- });
2669
- this.avatarConfig = signal({
2670
- color: 'primary',
2671
- look: 'rounded',
2672
- type: 'solid', // 'image' | 'text' | 'icon' | 'default'
2673
- });
2674
- this.validateContent = (content) => {
2675
- let isValid = true;
2676
- if (!content || content === '<p><br></p>') {
2677
- isValid = false;
2678
- }
2679
- return {
2680
- rule: 'callback',
2681
- result: isValid,
2682
- message: isValid ? '' : 'Please fill the content',
2683
- value: content,
2684
- };
2685
- };
2686
- }
2687
- ngOnInit() {
2688
- super.ngOnInit();
2689
- this.loadComments();
2690
- }
2691
- sanitizeHtml(htmlContent) {
2692
- if (!htmlContent)
2693
- return this.sanitize.bypassSecurityTrustHtml('');
2694
- return this.sanitize.bypassSecurityTrustHtml(htmlContent);
2695
- }
2696
- handleImageError(imageId) {
2697
- this.failedImageIds.update((ids) => [...ids, imageId]);
2698
- }
2699
- checkImageExists(imageId) {
2700
- return !this.failedImageIds().includes(imageId);
2701
- }
2702
- extractInitials(name) {
2703
- if (!name)
2704
- return '?';
2705
- // Handle the case where name is an object with fullName property
2706
- const nameStr = typeof name === 'object' && name.fullName ? name.fullName : String(name);
2707
- const words = nameStr.split(' ');
2708
- const initials = words.map((word) => word.charAt(0).toUpperCase());
2709
- return initials.join('');
2710
- }
2711
- async loadComments() {
2712
- this.isLoading.set(true);
2713
- try {
2714
- const response = await this.commentService.query(this.getPayload());
2715
- this.comments.set(response.items);
2716
- }
2717
- catch (error) {
2718
- console.error('Failed to load comments:', error);
2719
- this.toastService.show({
2720
- content: 'Failed to load comments. Please try again.',
2721
- color: 'danger',
2722
- location: 'bottom-center',
2723
- closeButton: true,
2724
- timeOut: 3000,
2725
- timeOutProgress: true,
2726
- });
2727
- this.comments.set([]);
2728
- }
2729
- finally {
2730
- setTimeout(() => {
2731
- this.isLoading.set(false);
2732
- }, 250);
2733
- }
2734
- }
2735
- editMessage(comment, reply) {
2736
- this.isReplyingMode.set(false);
2737
- this.activeReplyComment.set(undefined);
2738
- this.isEditingMode.set(true);
2739
- this.activeEditComment.set(comment);
2740
- const contentToEdit = reply ? reply.message?.content : comment.message?.content;
2741
- this.commentContent.set(contentToEdit || '');
2742
- this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
2743
- }
2744
- replyMessage(comment, reply) {
2745
- this.isEditingMode.set(false);
2746
- this.activeEditComment.set(undefined);
2747
- this.isReplyingMode.set(true);
2748
- this.activeReplyComment.set(comment);
2749
- if (reply) {
2750
- const author = reply.author.fullName || reply.author.id;
2751
- const mention = `<a data-id="${reply.id}">@${author}</a> `;
2752
- this.commentContent.set(mention);
2753
- this.wysiwyg()?.focus();
2754
- document.getElementsByClassName('ql-editor')[0].innerHTML = mention;
2755
- }
2756
- this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
2757
- }
2758
- async deleteComment(comment) {
2759
- const dialog = this.dialogService.open({
2760
- icon: 'fa-regular fa-warning',
2761
- content: 'Are you sure you want to delete this comment?',
2762
- title: 'Delete Comment',
2763
- type: 'danger',
2764
- orientation: 'horizontal',
2765
- buttons: [
2766
- {
2767
- text: 'Delete',
2768
- color: 'danger',
2769
- onClick: async (e) => {
2770
- e.handled = true;
2771
- e.source.text = 'Deleting...';
2772
- e.source.disabled = true;
2773
- e.source.loading = true;
2774
- try {
2775
- if (comment.id) {
2776
- await this.commentService.deleteOne(comment.id);
2777
- this.removeMessageById(comment.id);
2778
- this.toastService.show({
2779
- content: 'Comment deleted successfully.',
2780
- color: 'success',
2781
- location: 'bottom-center',
2782
- closeButton: true,
2783
- timeOut: 3000,
2784
- timeOutProgress: true,
2785
- });
2786
- if (this.isEditingMode() && this.activeEditComment()?.id === comment.id) {
2787
- this.resetReplyEditState();
2788
- }
2789
- dialog.close();
2790
- }
2791
- }
2792
- catch (error) {
2793
- this.toastService.show({
2794
- content: typeof error === 'string' ? error : 'Failed to delete comment!',
2795
- color: 'danger',
2796
- location: 'bottom-center',
2797
- closeButton: true,
2798
- timeOut: 3000,
2799
- timeOutProgress: true,
2800
- });
2801
- }
2802
- },
2803
- },
2804
- {
2805
- text: 'Cancel',
2806
- color: 'default',
2807
- autofocus: true,
2808
- onClick: (e) => {
2809
- dialog.close();
2810
- },
2811
- },
2812
- ],
2813
- closeButton: false,
2814
- });
2815
- }
2816
- async deleteReply(comment, reply) {
2817
- const dialog = this.dialogService.open({
2818
- icon: 'fa-regular fa-warning',
2819
- content: 'Are you sure you want to delete this reply?',
2820
- title: 'Delete Reply',
2821
- type: 'danger',
2822
- orientation: 'horizontal',
2823
- buttons: [
2824
- {
2825
- text: 'Delete',
2826
- color: 'danger',
2827
- onClick: async (e) => {
2828
- e.handled = true;
2829
- e.source.text = 'Deleting...';
2830
- e.source.disabled = true;
2831
- e.source.loading = true;
2832
- try {
2833
- if (reply.id) {
2834
- await this.commentService.deleteOne(reply.id);
2835
- this.removeMessageById(comment.id, reply.id);
2836
- this.toastService.show({
2837
- content: 'Comment deleted successfully.',
2838
- color: 'success',
2839
- location: 'bottom-center',
2840
- closeButton: true,
2841
- timeOut: 3000,
2842
- timeOutProgress: true,
2843
- });
2844
- dialog.close();
2845
- }
2846
- }
2847
- catch (error) {
2848
- this.toastService.show({
2849
- content: typeof error === 'string' ? error : 'Failed to delete comment!',
2850
- color: 'danger',
2851
- location: 'bottom-center',
2852
- closeButton: true,
2853
- timeOut: 3000,
2854
- timeOutProgress: true,
2855
- });
2856
- }
2857
- },
2858
- },
2859
- {
2860
- text: 'Cancel',
2861
- color: 'default',
2862
- autofocus: true,
2863
- onClick: (e) => {
2864
- dialog.close();
2865
- },
2866
- },
2867
- ],
2868
- closeButton: false,
2869
- });
2870
- }
2871
- resetReplyEditState() {
2872
- this.isEditingMode.set(false);
2873
- this.activeEditComment.set(undefined);
2874
- this.isReplyingMode.set(false);
2875
- this.activeReplyComment.set(undefined);
2876
- this.commentContent.set('');
2877
- document.getElementsByClassName('ql-editor')[0].innerHTML = '';
2878
- }
2879
- async toggleLike(comment, reply) {
2880
- try {
2881
- if (reply) {
2882
- if (reply.id) {
2883
- this.updateLikeStatus(comment.id, reply.id);
2884
- await this.commentService.like(reply.id);
2885
- }
2886
- }
2887
- else {
2888
- if (comment.id) {
2889
- this.updateLikeStatus(comment.id);
2890
- await this.commentService.like(comment.id);
2891
- }
2892
- }
2893
- }
2894
- catch (error) {
2895
- console.error('Failed to toggle like:', error);
2896
- // Revert the optimistic update if the server call fails
2897
- if (reply && reply.id) {
2898
- this.updateLikeStatus(comment.id, reply.id);
2899
- }
2900
- else if (comment.id) {
2901
- this.updateLikeStatus(comment.id);
2902
- }
2903
- }
2904
- }
2905
- updateLikeStatus(commentId, replyId) {
2906
- this.comments.update((commentsList) => {
2907
- return commentsList.map((comment) => {
2908
- if (comment.id === commentId) {
2909
- if (replyId && comment.replies) {
2910
- const updatedReplies = comment.replies.map((reply) => reply.id === replyId
2911
- ? {
2912
- ...reply,
2913
- isLiked: !reply.isLiked,
2914
- reactionsCount: reply.isLiked && reply.reactionsCount !== undefined
2915
- ? reply.reactionsCount - 1
2916
- : (reply.reactionsCount || 0) + 1,
2917
- }
2918
- : reply);
2919
- return {
2920
- ...comment,
2921
- replies: updatedReplies,
2922
- };
2923
- }
2924
- else {
2925
- return {
2926
- ...comment,
2927
- isLiked: !comment.isLiked,
2928
- reactionsCount: comment.isLiked && comment.reactionsCount !== undefined
2929
- ? comment.reactionsCount - 1
2930
- : (comment.reactionsCount || 0) + 1,
2931
- };
2932
- }
2933
- }
2934
- return comment;
2935
- });
2936
- });
2937
- }
2938
- removeMessageById(commentId, replyId) {
2939
- if (replyId) {
2940
- // Remove a reply from a comment
2941
- this.comments.update((commentsList) => commentsList.map((comment) => {
2942
- if (comment.id === commentId && comment.replies) {
2943
- return {
2944
- ...comment,
2945
- replies: comment.replies.filter((reply) => reply.id !== replyId),
2946
- };
2947
- }
2948
- return comment;
2949
- }));
2950
- }
2951
- else {
2952
- // Remove a whole comment
2953
- this.comments.update((commentsList) => commentsList.filter((comment) => comment.id !== commentId));
2969
+ addInputOverlay(type, message) {
2970
+ const input = this.inputRef();
2971
+ const icon = type === 'reply' ? 'fa-solid fa-reply' : 'fa-solid fa-pencil';
2972
+ if (input) {
2973
+ input.setActionBoxContainer(icon, message?.content || this.activeMessage()?.content || '');
2954
2974
  }
2955
2975
  }
2956
- async submitComment(isPrivate = false) {
2957
- if (!this.validateContent(this.commentContent()).result) {
2958
- return;
2959
- }
2960
- this.isSubmitting.set(true);
2961
- let memberLookup;
2962
- if (isPrivate) {
2963
- const popupConfig = {
2964
- header: true,
2965
- size: 'md',
2966
- draggable: true,
2967
- hasBackdrop: true,
2968
- closeButton: true,
2969
- closeOnBackdropClick: false,
2970
- };
2971
- const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
2972
- memberLookup = popup.data?.data?.lookup;
2973
- }
2974
- try {
2975
- if (this.isEditingMode() && this.activeEditComment()?.id) {
2976
- const payload = {
2977
- message: {
2978
- content: this.commentContent(),
2979
- contentType: 'text',
2980
- },
2981
- };
2982
- await this.commentService.updateOne(this.activeEditComment().id, payload);
2983
- this.hasCooldown.set(true);
2984
- this.isEditingMode.set(false);
2985
- this.activeEditComment.set(undefined);
2986
- }
2987
- else {
2988
- const payload = {
2989
- ...this.payload(),
2990
- content: this.commentContent(),
2991
- contentType: 'text',
2992
- isPrivate: isPrivate,
2993
- replyId: this.activeReplyComment()?.id ?? null,
2994
- };
2995
- await this.commentService.insertOne(payload);
2996
- this.hasCooldown.set(true);
2997
- this.isReplyingMode.set(false);
2998
- this.activeReplyComment.set(undefined);
2999
- }
3000
- // Reload comments to get the updated list with proper hierarchy
3001
- const response = await this.commentService.query(this.getPayload());
3002
- this.comments.set(response.items);
3003
- this.commentContent.set('');
3004
- document.getElementsByClassName('ql-editor')[0].innerHTML = '';
3005
- setTimeout(() => {
3006
- this.hasCooldown.set(false);
3007
- }, 1000);
3008
- }
3009
- catch (error) {
3010
- console.error('Error submitting comment:', error);
3011
- this.toastService.show({
3012
- content: typeof error === 'string' ? error : 'Failed to submit comment. Please try again.',
3013
- color: 'danger',
3014
- location: 'bottom-center',
3015
- closeButton: true,
3016
- timeOut: 3000,
3017
- timeOutProgress: true,
3018
- });
3019
- }
3020
- finally {
3021
- this.isSubmitting.set(false);
3022
- }
3023
- }
3024
- scrollMain() {
3025
- const comment = this.isReplyingMode() ? this.activeReplyComment() : this.activeEditComment();
3026
- if (comment && comment.id) {
3027
- const el = document.getElementById(comment.id);
3028
- if (el) {
3029
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
3030
- const content = el?.firstElementChild?.children[1];
3031
- if (content) {
3032
- const prevBg = content.style.background;
3033
- content.style.borderRadius = '0.25rem';
3034
- content.style.transition = 'background 1s ease-in-out';
3035
- content.style.background = `rgba(var(--ax-color-on-surface), var(--tw-bg-opacity))`;
3036
- setTimeout(() => {
3037
- content.style.background = prevBg || 'rgba(0, 0, 0, 0)';
3038
- }, 1000);
3039
- }
3040
- }
3041
- }
3042
- }
3043
- calcDefrenetTime(date) {
3044
- return date ? Date.now() - date.getTime() : undefined;
3045
- }
3046
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
3047
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentWidgetViewComponent, isStandalone: true, selector: "axm-comment-widget-view", viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }, { propertyName: "wysiwyg", first: true, predicate: ["w"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-mt-2 ax-p-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: "component", type: AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i6.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i5$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i7.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i9.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i1.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "component", type: i12.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i14.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i11.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i11.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i11.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i10.AXFormatPipe, name: "format" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2976
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2977
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: AXMChatPreviewComponent, isStandalone: true, selector: "axm-chat-preview", viewQueries: [{ propertyName: "inputRef", first: true, predicate: AXConversationInputComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n (onReplyClick)=\"addInputOverlay('reply', $event.data)\"\n [chatBoxHeight]=\"height() - 60 + 'px'\"\n ></ax-conversation-view>\n @if (!isChannel()) {\n <ax-conversation-input\n class=\"ax-p-1\"\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (ngModelChange)=\"handleTyping($event)\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n (onEnterPressed)=\"handleEnter()\"\n ></ax-conversation-input>\n }\n </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i11.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i11.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDomChangeDirective, selector: "[axDomChange]", outputs: ["axMutationObserver", "axResizeObserver"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "component", type: i2$4.AXConversationViewComponent, selector: "ax-conversation-view", inputs: ["chatBoxHeight", "isReplyArrowShown", "avatar"], outputs: ["onScrollEnd", "onActionMenuOpening", "onAction", "onReplyClick"] }, { kind: "component", type: i2$4.AXConversationInputComponent, selector: "ax-conversation-input", inputs: ["look", "placeholder", "maxLength", "hasAttachment", "hasVoice", "hasEmoji", "isLoading", "acceptFileType"], outputs: ["onSendClick", "onStartRecording", "onCancelRecording", "onEnterPressed"] }, { kind: "component", type: i2$4.AXConversationContainerComponent, selector: "ax-conversation-container", inputs: ["chatData"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i6.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3048
2978
  }
3049
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentWidgetViewComponent, decorators: [{
2979
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMChatPreviewComponent, decorators: [{
3050
2980
  type: Component,
3051
- args: [{ selector: 'axm-comment-widget-view', imports: [
3052
- AXWysiwygContainerComponent,
3053
- AXAvatarModule,
3054
- AXConversationModule,
3055
- AXSkeletonModule,
3056
- AXCommentModule,
3057
- AXButtonModule,
3058
- AXImageModule,
3059
- AXLoadingModule,
3060
- AXDropdownModule,
3061
- AXDecoratorModule,
3062
- AXWysiwygModule,
3063
- AXToolBarModule,
3064
- AXDropdownModule,
3065
- AXDropdownButtonModule,
3066
- AXValidationModule,
3067
- AsyncPipe,
3068
- AXFormModule,
3069
- FormsModule,
3070
- AXFormatModule,
3071
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-mt-2 ax-p-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"] }]
2981
+ args: [{ selector: 'axm-chat-preview', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, AXDomChangeDirective, AXConversationModule, AXLoadingModule], template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n (onReplyClick)=\"addInputOverlay('reply', $event.data)\"\n [chatBoxHeight]=\"height() - 60 + 'px'\"\n ></ax-conversation-view>\n @if (!isChannel()) {\n <ax-conversation-input\n class=\"ax-p-1\"\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (ngModelChange)=\"handleTyping($event)\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n (onEnterPressed)=\"handleEnter()\"\n ></ax-conversation-input>\n }\n </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
3072
2982
  }] });
3073
2983
 
3074
- var commentWidgetView_component = /*#__PURE__*/Object.freeze({
2984
+ var chatPreview_component = /*#__PURE__*/Object.freeze({
3075
2985
  __proto__: null,
3076
- AXMCommentWidgetViewComponent: AXMCommentWidgetViewComponent
2986
+ AXMChatPreviewComponent: AXMChatPreviewComponent
3077
2987
  });
3078
2988
 
3079
- const AXPCommentWidget = {
3080
- name: 'comment',
3081
- title: 'Comments',
3082
- description: 'Display and manage comments for entities',
3083
- type: 'view',
3084
- categories: [],
3085
- groups: [AXPWidgetGroupEnum.FormElement],
3086
- icon: 'fa-solid fa-comments',
3087
- properties: [],
3088
- components: {
3089
- view: {
3090
- component: () => Promise.resolve().then(function () { return commentWidgetView_component; }).then((c) => c.AXMCommentWidgetViewComponent),
3091
- },
3092
- },
3093
- };
3094
-
3095
2989
  function routesFactory() {
3096
2990
  const routes = [
3097
2991
  {
@@ -3100,6 +2994,7 @@ function routesFactory() {
3100
2994
  loadComponent: () => {
3101
2995
  return AXPRootLayoutComponent;
3102
2996
  },
2997
+ title: 'Chat',
3103
2998
  children: [
3104
2999
  {
3105
3000
  path: '',
@@ -3115,18 +3010,49 @@ function routesFactory() {
3115
3010
  ],
3116
3011
  },
3117
3012
  {
3118
- path: ':app/comments/m/:module/e/:id',
3119
- component: AXMCommentListViewComponent,
3013
+ path: ':app/comments/t/:refrenceType/i/:refrenceId/s/:subject',
3014
+ title: 'Comments',
3015
+ canActivate: [AXPAuthGuard],
3016
+ loadComponent: () => AXPRootLayoutComponent,
3017
+ // resolve: {
3018
+ // refrenceType: {
3019
+ // resolve: refrenceTypeResolver,
3020
+ // },
3021
+ // refrenceId: {
3022
+ // resolve: refrenceIdResolver,
3023
+ // },
3024
+ // subject: {
3025
+ // resolve: subjectResolver,
3026
+ // },
3027
+ // },
3028
+ children: [
3029
+ {
3030
+ path: '',
3031
+ loadComponent: () => Promise.resolve().then(function () { return comment_component; }).then((c) => c.AXMCommentComponent),
3032
+ },
3033
+ ],
3034
+ },
3035
+ {
3036
+ path: ':app/comments/',
3037
+ title: 'Comments',
3038
+ canActivate: [AXPAuthGuard],
3039
+ loadComponent: () => AXPRootLayoutComponent,
3040
+ children: [
3041
+ {
3042
+ path: '',
3043
+ loadComponent: () => Promise.resolve().then(function () { return comment_component; }).then((c) => c.AXMCommentComponent),
3044
+ },
3045
+ ],
3120
3046
  },
3121
3047
  ];
3122
3048
  return routes;
3123
3049
  }
3124
3050
  class AXMConversationModule {
3125
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3126
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, exports: [
3051
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3052
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModule, imports: [i2$1.AXPLayoutBuilderModule, i2$5.AXPWorkflowModule], exports: [
3127
3053
  // Modules
3128
3054
  RouterModule] }); }
3129
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, providers: [
3055
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModule, providers: [
3130
3056
  // Services
3131
3057
  {
3132
3058
  provide: AXMMessageService,
@@ -3151,6 +3077,11 @@ class AXMConversationModule {
3151
3077
  useClass: AXMConversationModuleEntityProvider,
3152
3078
  multi: true,
3153
3079
  },
3080
+ {
3081
+ provide: AXP_MENU_PROVIDER,
3082
+ useClass: AXMMenuProvider,
3083
+ multi: true,
3084
+ },
3154
3085
  {
3155
3086
  provide: ROUTES,
3156
3087
  multi: true,
@@ -3165,15 +3096,37 @@ class AXMConversationModule {
3165
3096
  // Conversation Module
3166
3097
  importProvidersFrom(AXConversationModule.forRoot()),
3167
3098
  DatePipe,
3168
- ], imports: [
3099
+ ], imports: [AXPLayoutBuilderModule.forChild({
3100
+ widgets: [AXPCommentWidget],
3101
+ }),
3102
+ AXPWorkflowModule.forChild({
3103
+ actions: {
3104
+ 'show-comment-popup-action': AXMCommentPopupStartAction,
3105
+ },
3106
+ workflows: {
3107
+ 'show-comment-popup': AXMCommentPopupWorkflow,
3108
+ },
3109
+ }),
3169
3110
  // Modules
3170
3111
  RouterModule] }); }
3171
3112
  }
3172
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, decorators: [{
3113
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: AXMConversationModule, decorators: [{
3173
3114
  type: NgModule,
3174
3115
  args: [{
3175
3116
  declarations: [],
3176
- imports: [],
3117
+ imports: [
3118
+ AXPLayoutBuilderModule.forChild({
3119
+ widgets: [AXPCommentWidget],
3120
+ }),
3121
+ AXPWorkflowModule.forChild({
3122
+ actions: {
3123
+ 'show-comment-popup-action': AXMCommentPopupStartAction,
3124
+ },
3125
+ workflows: {
3126
+ 'show-comment-popup': AXMCommentPopupWorkflow,
3127
+ },
3128
+ }),
3129
+ ],
3177
3130
  exports: [
3178
3131
  // Modules
3179
3132
  RouterModule,
@@ -3203,6 +3156,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3203
3156
  useClass: AXMConversationModuleEntityProvider,
3204
3157
  multi: true,
3205
3158
  },
3159
+ {
3160
+ provide: AXP_MENU_PROVIDER,
3161
+ useClass: AXMMenuProvider,
3162
+ multi: true,
3163
+ },
3206
3164
  {
3207
3165
  provide: ROUTES,
3208
3166
  multi: true,
@@ -3225,5 +3183,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3225
3183
  * Generated bundle index. Do not edit.
3226
3184
  */
3227
3185
 
3228
- export { AXMChatComponent, AXMChatItemComponent, AXMChatPreviewComponent, AXMChatService, AXMChatServiceImpl, AXMCommentListViewComponent, AXMCommentLookupPopup, AXMCommentService, AXMCommentServiceImpl, AXMCommentWidgetViewComponent, AXMConversationModule, AXMMessageService, AXMMessageServiceImpl, AXMRoomService, AXMRoomServiceImpl, AXPCommentWidget, RootConfig, messageFactory, roomFactory };
3186
+ export { AXMChatComponent, AXMChatItemComponent, AXMChatPreviewComponent, AXMChatService, AXMChatServiceImpl, AXMCommentComponent, AXMCommentPopupComponent, AXMCommentPopupStartAction, AXMCommentPopupWorkflow, AXMCommentService, AXMCommentServiceImpl, AXMCommentWidgetViewComponent, AXMConversationModule, AXMMessageService, AXMMessageServiceImpl, AXMRoomService, AXMRoomServiceImpl, AXMUserLookupPopup, AXPCommentWidget, RootConfig, messageFactory, roomFactory };
3229
3187
  //# sourceMappingURL=acorex-modules-conversation.mjs.map