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