@acorex/modules 20.0.4 → 20.0.6
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/index.d.ts +157 -217
- package/fesm2022/{acorex-modules-application-management-module-designer.component-CvFiEI1a.mjs → acorex-modules-application-management-module-designer.component-oGgCaUDu.mjs} +2 -2
- package/fesm2022/{acorex-modules-application-management-module-designer.component-CvFiEI1a.mjs.map → acorex-modules-application-management-module-designer.component-oGgCaUDu.mjs.map} +1 -1
- package/fesm2022/acorex-modules-application-management.mjs +1 -1
- package/fesm2022/{acorex-modules-auth-acorex-modules-auth-B10cRw3M.mjs → acorex-modules-auth-acorex-modules-auth-Cj4td3eM.mjs} +9 -9
- package/fesm2022/{acorex-modules-auth-acorex-modules-auth-B10cRw3M.mjs.map → acorex-modules-auth-acorex-modules-auth-Cj4td3eM.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-app-chooser.component-C-HPf2jz.mjs → acorex-modules-auth-app-chooser.component-C60idNYx.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-app-chooser.component-C-HPf2jz.mjs.map → acorex-modules-auth-app-chooser.component-C60idNYx.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-login.module-eC1_ukrb.mjs → acorex-modules-auth-login.module-siRdi16m.mjs} +4 -4
- package/fesm2022/{acorex-modules-auth-login.module-eC1_ukrb.mjs.map → acorex-modules-auth-login.module-siRdi16m.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-master.layout-DpfqRgAl.mjs → acorex-modules-auth-master.layout-BJCAe5Ai.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-master.layout-DpfqRgAl.mjs.map → acorex-modules-auth-master.layout-BJCAe5Ai.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-password.component-DAB9dWB8.mjs → acorex-modules-auth-password.component-6-EEsfc2.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-password.component-DAB9dWB8.mjs.map → acorex-modules-auth-password.component-6-EEsfc2.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-password.component-rE9yrcGW.mjs → acorex-modules-auth-password.component-VhDUmkMc.mjs} +3 -3
- package/fesm2022/acorex-modules-auth-password.component-VhDUmkMc.mjs.map +1 -0
- package/fesm2022/{acorex-modules-auth-routes-EGvejNu9.mjs → acorex-modules-auth-routes-BwBn4o81.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-routes-EGvejNu9.mjs.map → acorex-modules-auth-routes-BwBn4o81.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-two-factor.module-DF3_fgzH.mjs → acorex-modules-auth-two-factor.module-D7NgZHmC.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-two-factor.module-DF3_fgzH.mjs.map → acorex-modules-auth-two-factor.module-D7NgZHmC.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-user-sessions.component-BPb-WS4k.mjs → acorex-modules-auth-user-sessions.component-DhlBCxly.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-user-sessions.component-BPb-WS4k.mjs.map → acorex-modules-auth-user-sessions.component-DhlBCxly.mjs.map} +1 -1
- package/fesm2022/acorex-modules-auth.mjs +1 -1
- package/fesm2022/acorex-modules-conversation.mjs +1762 -1568
- package/fesm2022/acorex-modules-conversation.mjs.map +1 -1
- package/fesm2022/acorex-modules-dashboard-management.mjs +9 -9
- package/fesm2022/acorex-modules-dashboard-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-notification-management.mjs +3 -3
- package/fesm2022/acorex-modules-notification-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-organization-management-role.entity-BDErzNv-.mjs.map +1 -1
- package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-DqpAYmHM.mjs → acorex-modules-platform-management-acorex-modules-platform-management-Dfux0tkB.mjs} +6 -6
- package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-Dfux0tkB.mjs.map +1 -0
- package/fesm2022/{acorex-modules-platform-management-list-version.component-DidjxkmH.mjs → acorex-modules-platform-management-list-version.component-CSNKxghv.mjs} +2 -2
- package/fesm2022/{acorex-modules-platform-management-list-version.component-DidjxkmH.mjs.map → acorex-modules-platform-management-list-version.component-CSNKxghv.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-platform-management-settings.provider-DcHKHrSK.mjs → acorex-modules-platform-management-settings.provider-C4RaOgkr.mjs} +35 -25
- package/fesm2022/acorex-modules-platform-management-settings.provider-C4RaOgkr.mjs.map +1 -0
- package/fesm2022/acorex-modules-platform-management.mjs +1 -1
- package/fesm2022/acorex-modules-report-management-report-runner-root-page.component-CtEDcg_B.mjs +589 -0
- package/fesm2022/acorex-modules-report-management-report-runner-root-page.component-CtEDcg_B.mjs.map +1 -0
- package/fesm2022/acorex-modules-report-management.mjs +208 -0
- package/fesm2022/acorex-modules-report-management.mjs.map +1 -0
- package/fesm2022/acorex-modules-security-management.mjs +582 -72
- package/fesm2022/acorex-modules-security-management.mjs.map +1 -1
- package/fesm2022/{acorex-modules-workflow-management-task-board.page-R4x5Rq-D.mjs → acorex-modules-workflow-management-task-board.page-Bugxqd0W.mjs} +2 -2
- package/fesm2022/acorex-modules-workflow-management-task-board.page-Bugxqd0W.mjs.map +1 -0
- package/fesm2022/acorex-modules-workflow-management.mjs +1 -1
- package/package.json +5 -1
- package/report-management/README.md +356 -0
- package/report-management/index.d.ts +39 -0
- package/security-management/index.d.ts +162 -2
- package/fesm2022/acorex-modules-auth-password.component-rE9yrcGW.mjs.map +0 -1
- package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-DqpAYmHM.mjs.map +0 -1
- package/fesm2022/acorex-modules-platform-management-settings.provider-DcHKHrSK.mjs.map +0 -1
- package/fesm2022/acorex-modules-security-management-permissions-editor.component-B4TdyHtv.mjs +0 -218
- package/fesm2022/acorex-modules-security-management-permissions-editor.component-B4TdyHtv.mjs.map +0 -1
- package/fesm2022/acorex-modules-security-management-permissions-widget-column.component-DTMj6gxb.mjs +0 -41
- package/fesm2022/acorex-modules-security-management-permissions-widget-column.component-DTMj6gxb.mjs.map +0 -1
- package/fesm2022/acorex-modules-security-management-permissions-widget-designer.component-BJdbKpRQ.mjs +0 -20
- package/fesm2022/acorex-modules-security-management-permissions-widget-designer.component-BJdbKpRQ.mjs.map +0 -1
- package/fesm2022/acorex-modules-security-management-permissions-widget-edit.component-GTmDlgWW.mjs +0 -51
- package/fesm2022/acorex-modules-security-management-permissions-widget-edit.component-GTmDlgWW.mjs.map +0 -1
- package/fesm2022/acorex-modules-security-management-permissions-widget-print.component-BZ75aI18.mjs +0 -21
- package/fesm2022/acorex-modules-security-management-permissions-widget-print.component-BZ75aI18.mjs.map +0 -1
- package/fesm2022/acorex-modules-security-management-permissions-widget-view.component-CYQqerAX.mjs +0 -20
- package/fesm2022/acorex-modules-security-management-permissions-widget-view.component-CYQqerAX.mjs.map +0 -1
- package/fesm2022/acorex-modules-workflow-management-task-board.page-R4x5Rq-D.mjs.map +0 -1
@@ -1,69 +1,70 @@
|
|
1
|
-
import * as
|
1
|
+
import * as i2$4 from '@acorex/components/conversation';
|
2
|
+
import { AXConversationModule } from '@acorex/components/conversation';
|
3
|
+
import { AXPSessionService, AXPAuthGuard } from '@acorex/platform/auth';
|
4
|
+
import { AXMEntityCrudServiceImpl, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
|
5
|
+
import { AXPRootLayoutComponent } from '@acorex/platform/themes/default';
|
6
|
+
import * as i1$1 from '@angular/common';
|
7
|
+
import { AsyncPipe, DatePipe, CommonModule } from '@angular/common';
|
8
|
+
import * as i0 from '@angular/core';
|
9
|
+
import { Injectable, inject, signal, Component, viewChild, computed, ChangeDetectionStrategy, input, output, effect, afterNextRender, importProvidersFrom, NgModule } from '@angular/core';
|
10
|
+
import * as i2$3 from '@angular/router';
|
11
|
+
import { ActivatedRoute, Router, NavigationEnd, RouterModule, ROUTES } from '@angular/router';
|
12
|
+
import { createAllQueryView, AXPEntityCommandScope, AXPEntityQueryType } from '@acorex/platform/common';
|
13
|
+
import * as i2 from '@acorex/platform/layout/builder';
|
14
|
+
import { AXPWidgetsCatalog, AXPLayoutBuilderModule, AXPLayoutWidgetComponent, AXPWidgetGroupEnum } from '@acorex/platform/layout/builder';
|
2
15
|
import { AXResizableDirective } from '@acorex/cdk/resizable';
|
3
|
-
import * as
|
16
|
+
import * as i6 from '@acorex/components/avatar';
|
4
17
|
import { AXAvatarModule } from '@acorex/components/avatar';
|
5
|
-
import * as
|
18
|
+
import * as i2$2 from '@acorex/components/badge';
|
6
19
|
import { AXBadgeModule } from '@acorex/components/badge';
|
7
|
-
import * as
|
20
|
+
import * as i5$1 from '@acorex/components/button';
|
8
21
|
import { AXButtonModule } from '@acorex/components/button';
|
9
|
-
import * as
|
22
|
+
import * as i4 from '@acorex/components/comment';
|
10
23
|
import { AXCommentModule } from '@acorex/components/comment';
|
11
|
-
import * as i2$2 from '@acorex/components/conversation';
|
12
|
-
import { AXConversationModule } from '@acorex/components/conversation';
|
13
24
|
import * as i5 from '@acorex/components/decorators';
|
14
25
|
import { AXDecoratorModule } from '@acorex/components/decorators';
|
15
|
-
import * as
|
26
|
+
import * as i9 from '@acorex/components/dropdown';
|
16
27
|
import { AXDropdownModule } from '@acorex/components/dropdown';
|
17
|
-
import * as
|
28
|
+
import * as i14 from '@acorex/components/dropdown-button';
|
18
29
|
import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
|
19
|
-
import * as
|
30
|
+
import * as i11 from '@acorex/components/form';
|
20
31
|
import { AXFormModule } from '@acorex/components/form';
|
21
|
-
import * as
|
32
|
+
import * as i7 from '@acorex/components/image';
|
22
33
|
import { AXImageModule } from '@acorex/components/image';
|
23
34
|
import { AXLabelModule } from '@acorex/components/label';
|
24
|
-
import * as
|
35
|
+
import * as i8 from '@acorex/components/loading';
|
25
36
|
import { AXLoadingModule } from '@acorex/components/loading';
|
26
|
-
import
|
37
|
+
import { AXPopupService } from '@acorex/components/popup';
|
38
|
+
import * as i6$1 from '@acorex/components/search-box';
|
27
39
|
import { AXSearchBoxModule } from '@acorex/components/search-box';
|
28
40
|
import { AXSelectBoxModule } from '@acorex/components/select-box';
|
29
|
-
import * as
|
41
|
+
import * as i2$1 from '@acorex/components/skeleton';
|
30
42
|
import { AXSkeletonModule } from '@acorex/components/skeleton';
|
31
|
-
import * as i7 from '@acorex/components/tabs';
|
43
|
+
import * as i7$1 from '@acorex/components/tabs';
|
32
44
|
import { AXTabsModule } from '@acorex/components/tabs';
|
33
45
|
import { AXTextBoxModule } from '@acorex/components/text-box';
|
34
46
|
import * as i12 from '@acorex/components/toolbar';
|
35
47
|
import { AXToolBarModule } from '@acorex/components/toolbar';
|
36
|
-
import * as
|
37
|
-
import {
|
38
|
-
import * as
|
48
|
+
import * as i1 from '@acorex/components/wysiwyg';
|
49
|
+
import { AXWysiwygModule, AXWysiwygContainerComponent } from '@acorex/components/wysiwyg';
|
50
|
+
import * as i10 from '@acorex/core/format';
|
39
51
|
import { AXFormatModule } from '@acorex/core/format';
|
52
|
+
import * as i3 from '@acorex/core/translation';
|
40
53
|
import { AXTranslationModule } from '@acorex/core/translation';
|
41
|
-
import
|
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';
|
47
|
-
import * as i14 from '@angular/common';
|
48
|
-
import { AsyncPipe, CommonModule } from '@angular/common';
|
49
|
-
import * as i0 from '@angular/core';
|
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';
|
52
|
-
import { FormsModule } from '@angular/forms';
|
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, AXPLayoutWidgetComponent, AXPWidgetGroupEnum } from '@acorex/platform/layout/builder';
|
54
|
+
import { AXUnsubscriber } from '@acorex/core/utils';
|
58
55
|
import { AXMUsersEntityService } from '@acorex/modules/security-management';
|
59
|
-
import {
|
60
|
-
import {
|
56
|
+
import { AXPUserAvatarComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutToolbarComponent } from '@acorex/platform/layout/components';
|
57
|
+
import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageLayoutBase } from '@acorex/platform/layout/views';
|
58
|
+
import * as i13 from '@angular/forms';
|
59
|
+
import { FormsModule } from '@angular/forms';
|
60
|
+
import { Subject, filter, startWith } from 'rxjs';
|
61
61
|
import { AXDialogService } from '@acorex/components/dialog';
|
62
|
-
import { AXPopupService } from '@acorex/components/popup';
|
63
62
|
import { AXToastService } from '@acorex/components/toast';
|
64
63
|
import { AXPlatform } from '@acorex/core/platform';
|
65
64
|
import { DomSanitizer } from '@angular/platform-browser';
|
66
65
|
import { AXBasePageComponent } from '@acorex/components/page';
|
66
|
+
import { AXDomChangeDirective } from '@acorex/cdk/dom';
|
67
|
+
import { AXFileService } from '@acorex/core/file';
|
67
68
|
import { AXValidationModule } from '@acorex/core/validation';
|
68
69
|
|
69
70
|
const config = {
|
@@ -677,11 +678,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
|
|
677
678
|
}], ctorParameters: () => [{ type: i0.Injector }] });
|
678
679
|
|
679
680
|
/**
|
680
|
-
* Abstract
|
681
|
+
* Abstract Comment Service defining the contract for comment functionality
|
681
682
|
*/
|
682
|
-
class
|
683
|
+
class AXMCommentService {
|
683
684
|
}
|
684
|
-
class
|
685
|
+
class AXMCommentServiceImpl {
|
685
686
|
constructor() {
|
686
687
|
this.roomService = inject(AXMRoomService);
|
687
688
|
this.messageService = inject(AXMMessageService);
|
@@ -701,49 +702,92 @@ class AXMChatServiceImpl {
|
|
701
702
|
const user = await this.usersService.getOne(userId);
|
702
703
|
return {
|
703
704
|
id: user.id,
|
704
|
-
|
705
|
+
userName: user.username || user.id,
|
706
|
+
firstName: user.firstName || '',
|
707
|
+
lastName: user.lastName || '',
|
708
|
+
fullName: `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'Unknown User',
|
709
|
+
avatar: '',
|
705
710
|
};
|
706
711
|
}
|
707
712
|
catch (error) {
|
708
713
|
console.error(`Failed to get user info for ID: ${userId}`, error);
|
709
714
|
return {
|
710
715
|
id: userId,
|
711
|
-
|
716
|
+
userName: userId,
|
717
|
+
firstName: 'Unknown',
|
718
|
+
lastName: 'User',
|
719
|
+
fullName: 'Unknown User',
|
720
|
+
avatar: '',
|
712
721
|
};
|
713
722
|
}
|
714
723
|
}
|
715
|
-
|
724
|
+
formatRoomId(entityId, instanceId, roomType = 'default') {
|
725
|
+
return `${roomType}:${entityId}:${instanceId}`;
|
726
|
+
}
|
727
|
+
async formatComment(message) {
|
716
728
|
const currentUserId = this.getCurrentUser().id;
|
729
|
+
// Check if the current user has liked this message
|
730
|
+
const isLiked = (message.reactions || []).some((reaction) => reaction.author.id === currentUserId && reaction.type === 'like');
|
731
|
+
// Count total reactions
|
732
|
+
const reactionsCount = (message.reactions || []).filter((r) => r.type === 'like').length;
|
733
|
+
// Get the full user info for the author
|
734
|
+
const userInfo = await this.getUserInfo(message.author.id);
|
717
735
|
return {
|
718
736
|
...message,
|
737
|
+
author: {
|
738
|
+
...message.author,
|
739
|
+
...userInfo,
|
740
|
+
},
|
719
741
|
isFromCurrentUser: message.author.id === currentUserId,
|
720
742
|
formattedDate: new Date(message.createdAt || new Date()).toLocaleString(),
|
721
|
-
|
743
|
+
isLiked,
|
744
|
+
reactionsCount,
|
745
|
+
replies: [],
|
722
746
|
};
|
723
747
|
}
|
724
|
-
async
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
}
|
748
|
+
async buildCommentHierarchy(comments) {
|
749
|
+
// Create a map for quick lookup of comments by ID
|
750
|
+
const commentMap = new Map();
|
751
|
+
const rootComments = [];
|
752
|
+
// First pass: add all comments to the map
|
753
|
+
for (const comment of comments) {
|
754
|
+
commentMap.set(comment.id, comment);
|
755
|
+
// Initialize the replies array if not already present
|
756
|
+
if (!comment.replies) {
|
757
|
+
comment.replies = [];
|
758
|
+
}
|
759
|
+
// If this is a root comment (no replyId), add it to the root comments array
|
760
|
+
if (!comment.replyId) {
|
761
|
+
rootComments.push(comment);
|
762
|
+
}
|
735
763
|
}
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
764
|
+
// Second pass: build the hierarchy by adding replies to their parent comments
|
765
|
+
for (const comment of comments) {
|
766
|
+
if (comment.replyId) {
|
767
|
+
const parentComment = commentMap.get(comment.replyId);
|
768
|
+
if (parentComment && parentComment.replies) {
|
769
|
+
parentComment.replies.push(comment);
|
770
|
+
}
|
771
|
+
else {
|
772
|
+
// If parent not found (might be deleted), treat as a root comment
|
773
|
+
rootComments.push(comment);
|
774
|
+
}
|
775
|
+
}
|
776
|
+
}
|
777
|
+
// Sort the root comments and their replies by creation date (newest first)
|
778
|
+
const sortByDate = (a, b) => {
|
779
|
+
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
780
|
+
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
781
|
+
return dateB - dateA;
|
782
|
+
};
|
783
|
+
rootComments.sort(sortByDate);
|
784
|
+
// Sort the replies within each comment
|
785
|
+
for (const comment of rootComments) {
|
786
|
+
if (comment.replies && comment.replies.length > 0) {
|
787
|
+
comment.replies.sort(sortByDate);
|
788
|
+
}
|
746
789
|
}
|
790
|
+
return rootComments;
|
747
791
|
}
|
748
792
|
// === Room Operations ===
|
749
793
|
async getRoomList(skip = 0, take = 100) {
|
@@ -755,23 +799,41 @@ class AXMChatServiceImpl {
|
|
755
799
|
async createRoom(roomData) {
|
756
800
|
return this.roomService.insertOne(roomData);
|
757
801
|
}
|
758
|
-
async
|
759
|
-
const
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
802
|
+
async ensureRoomExists(entityId, instanceId, roomType = 'default') {
|
803
|
+
const roomId = this.formatRoomId(entityId, instanceId, roomType);
|
804
|
+
try {
|
805
|
+
// Check if room exists
|
806
|
+
await this.getRoomDetails(roomId);
|
807
|
+
return roomId;
|
808
|
+
}
|
809
|
+
catch (error) {
|
810
|
+
// If room doesn't exist, create it
|
811
|
+
const roomData = {
|
812
|
+
id: roomId, // Provide the ID directly for comments
|
813
|
+
title: `Comments for ${entityId}:${instanceId}`,
|
814
|
+
members: [this.getCurrentUser()],
|
815
|
+
};
|
816
|
+
// Create a new room or return the ID if it already exists
|
817
|
+
try {
|
818
|
+
const result = await this.createRoom(roomData);
|
819
|
+
return typeof result === 'string' ? result : roomId;
|
820
|
+
}
|
821
|
+
catch (e) {
|
822
|
+
// If we get a duplicate error, the room was created by another request
|
823
|
+
return roomId;
|
824
|
+
}
|
825
|
+
}
|
771
826
|
}
|
772
|
-
// ===
|
773
|
-
async
|
774
|
-
|
827
|
+
// === Comment Operations ===
|
828
|
+
async query(queryRequest) {
|
829
|
+
const { params, skip = 0, take = 20 } = queryRequest;
|
830
|
+
const { entityId, instanceId, roomType = 'default' } = params || {};
|
831
|
+
if (!entityId || !instanceId) {
|
832
|
+
throw new Error('EntityId and InstanceId are required for querying comments');
|
833
|
+
}
|
834
|
+
const roomId = this.formatRoomId(entityId, instanceId, roomType);
|
835
|
+
// Fetch messages for this room
|
836
|
+
const result = await this.messageService.query({
|
775
837
|
skip,
|
776
838
|
take,
|
777
839
|
filter: {
|
@@ -780,816 +842,671 @@ class AXMChatServiceImpl {
|
|
780
842
|
operator: { type: 'equal' },
|
781
843
|
},
|
782
844
|
});
|
845
|
+
// Transform messages to comments
|
846
|
+
const comments = await Promise.all(result.items.map((message) => this.formatComment(message)));
|
847
|
+
// Build comment hierarchy
|
848
|
+
const hierarchicalComments = await this.buildCommentHierarchy(comments);
|
849
|
+
return {
|
850
|
+
items: hierarchicalComments,
|
851
|
+
total: result.total,
|
852
|
+
};
|
783
853
|
}
|
784
|
-
async
|
785
|
-
const
|
854
|
+
async getOne(id) {
|
855
|
+
const message = await this.messageService.getOne(id);
|
856
|
+
return this.formatComment(message);
|
857
|
+
}
|
858
|
+
async insertOne(commentData) {
|
859
|
+
const { entityId, instanceId, roomType = 'default', content, contentType = 'text', replyId, isPrivate, } = commentData;
|
860
|
+
// Ensure the room exists
|
861
|
+
const roomId = await this.ensureRoomExists(entityId, instanceId, roomType);
|
862
|
+
// Create the message content
|
786
863
|
const messageContent = {
|
787
864
|
content,
|
788
865
|
contentType,
|
789
866
|
};
|
867
|
+
// Create the message object
|
790
868
|
const message = {
|
791
869
|
roomId,
|
792
870
|
message: messageContent,
|
793
|
-
author,
|
794
|
-
replyId,
|
871
|
+
author: this.getCurrentUser(),
|
872
|
+
replyId: replyId || null,
|
873
|
+
reactions: [],
|
874
|
+
seen: [],
|
795
875
|
};
|
876
|
+
// Insert the message
|
796
877
|
const messageId = await this.messageService.insertOne(message);
|
797
|
-
return
|
878
|
+
// Fetch the full message to return as a comment entity
|
879
|
+
const insertedMessage = await this.messageService.getOne(messageId);
|
880
|
+
return this.formatComment(insertedMessage);
|
798
881
|
}
|
799
|
-
async
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
882
|
+
async updateOne(id, update) {
|
883
|
+
// If content is being updated, format it correctly
|
884
|
+
let messageUpdate = {};
|
885
|
+
if (update.message?.content) {
|
886
|
+
messageUpdate.message = {
|
887
|
+
content: update.message.content,
|
888
|
+
contentType: update.message.contentType || 'text',
|
889
|
+
};
|
890
|
+
}
|
891
|
+
// Apply the update
|
892
|
+
const result = await this.messageService.updateOne(id, messageUpdate);
|
893
|
+
// Return as a comment entity
|
894
|
+
return this.formatComment(result);
|
805
895
|
}
|
806
|
-
async
|
896
|
+
async deleteOne(id) {
|
807
897
|
try {
|
808
|
-
await this.messageService.deleteOne(
|
898
|
+
await this.messageService.deleteOne(id);
|
809
899
|
return true;
|
810
900
|
}
|
811
901
|
catch (error) {
|
812
|
-
console.error('Failed to delete
|
902
|
+
console.error('Failed to delete comment:', error);
|
813
903
|
return false;
|
814
904
|
}
|
815
905
|
}
|
816
|
-
|
817
|
-
|
818
|
-
const message = await this.messageService.getOne(messageId);
|
819
|
-
// Check if this user has already seen the message
|
820
|
-
const alreadySeen = (message.seen || []).some((seen) => seen.author.id === author.id);
|
821
|
-
if (alreadySeen) {
|
822
|
-
return message;
|
823
|
-
}
|
824
|
-
const seen = message.seen || [];
|
825
|
-
const updatedSeen = [...seen, { author, type: 'read' }];
|
826
|
-
return this.messageService.updateOne(messageId, { seen: updatedSeen });
|
827
|
-
}
|
828
|
-
async addReaction(messageId, type) {
|
906
|
+
// === Comment-Specific Operations ===
|
907
|
+
async like(messageId) {
|
829
908
|
const author = this.getCurrentUser();
|
830
909
|
const message = await this.messageService.getOne(messageId);
|
831
|
-
// Check if
|
832
|
-
const
|
910
|
+
// Check if the user has already liked this message
|
911
|
+
const reactions = message.reactions || [];
|
912
|
+
const existingReaction = reactions.find((r) => r.author.id === author.id && r.type === 'like');
|
913
|
+
let updatedReactions;
|
833
914
|
if (existingReaction) {
|
834
|
-
// If the reaction
|
835
|
-
|
915
|
+
// If the reaction exists, remove it (unlike)
|
916
|
+
updatedReactions = reactions.filter((r) => !(r.author.id === author.id && r.type === 'like'));
|
836
917
|
}
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
const
|
843
|
-
|
844
|
-
|
845
|
-
const updatedReactions = reactions.filter((reaction) => !(reaction.author.id === author.id && reaction.type === type));
|
846
|
-
return this.messageService.updateOne(messageId, { reactions: updatedReactions });
|
918
|
+
else {
|
919
|
+
// If the reaction doesn't exist, add it (like)
|
920
|
+
updatedReactions = [...reactions, { author, type: 'like' }];
|
921
|
+
}
|
922
|
+
// Update the message with the new reactions
|
923
|
+
const updatedMessage = await this.messageService.updateOne(messageId, { reactions: updatedReactions });
|
924
|
+
// Return as a comment entity
|
925
|
+
return this.formatComment(updatedMessage);
|
847
926
|
}
|
848
|
-
|
849
|
-
|
927
|
+
async getCommentCount(entityId, instanceId, roomType = 'default') {
|
928
|
+
const roomId = this.formatRoomId(entityId, instanceId, roomType);
|
850
929
|
try {
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
})
|
862
|
-
.map((message) => this.markAsSeen(message.id));
|
863
|
-
await Promise.all(markPromises);
|
864
|
-
return true;
|
930
|
+
const result = await this.messageService.query({
|
931
|
+
skip: 0,
|
932
|
+
take: 1,
|
933
|
+
filter: {
|
934
|
+
field: 'roomId',
|
935
|
+
value: roomId,
|
936
|
+
operator: { type: 'equal' },
|
937
|
+
},
|
938
|
+
});
|
939
|
+
return result.total;
|
865
940
|
}
|
866
941
|
catch (error) {
|
867
|
-
console.error('Failed to
|
868
|
-
return
|
942
|
+
console.error('Failed to get comment count:', error);
|
943
|
+
return 0;
|
869
944
|
}
|
870
945
|
}
|
871
|
-
async
|
872
|
-
const
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
const isFromCurrentUser = message.author.id === currentUserId;
|
877
|
-
const isAlreadySeen = (message.seen || []).some((seen) => seen.author.id === currentUserId);
|
878
|
-
return !isFromCurrentUser && !isAlreadySeen;
|
879
|
-
}).length;
|
880
|
-
}
|
881
|
-
async getChatRoom(roomId) {
|
882
|
-
const room = await this.getRoomDetails(roomId);
|
883
|
-
const messages = await this.listMessages(roomId, 0, 1);
|
884
|
-
const lastMessage = messages.items.length > 0 ? this.formatMessage(messages.items[0]) : undefined;
|
885
|
-
const unreadCount = await this.getUnreadCount(roomId);
|
886
|
-
const roomMembers = await Promise.all(room.members.map((member) => this.formatMember(member)));
|
887
|
-
return {
|
888
|
-
...room,
|
889
|
-
lastMessage,
|
890
|
-
unreadCount,
|
891
|
-
roomMembers,
|
892
|
-
};
|
893
|
-
}
|
894
|
-
async getAllChatRooms(skip = 0, take = 100) {
|
895
|
-
const rooms = await this.getRoomList(skip, take);
|
896
|
-
const chatRooms = await Promise.all(rooms.items.map(async (room) => {
|
897
|
-
const messages = await this.listMessages(room.id, 0, 1);
|
898
|
-
const lastMessage = messages.items.length > 0 ? this.formatMessage(messages.items[0]) : undefined;
|
899
|
-
const unreadCount = await this.getUnreadCount(room.id);
|
900
|
-
const roomMembers = await Promise.all(room.members.map((member) => this.formatMember(member)));
|
901
|
-
return {
|
902
|
-
...room,
|
903
|
-
lastMessage,
|
904
|
-
unreadCount,
|
905
|
-
roomMembers,
|
906
|
-
};
|
907
|
-
}));
|
908
|
-
return {
|
909
|
-
items: chatRooms,
|
910
|
-
total: rooms.total,
|
911
|
-
};
|
912
|
-
}
|
913
|
-
async getChatMessages(roomId, skip = 0, take = 20) {
|
914
|
-
const result = await this.listMessages(roomId, skip, take);
|
915
|
-
const formattedMessages = result.items.map((message) => this.formatMessage(message));
|
916
|
-
return {
|
917
|
-
items: formattedMessages,
|
918
|
-
total: result.total,
|
946
|
+
async getAllCommentsWithReplies(entityId, instanceId, roomType = 'default', skip = 0, take = 100) {
|
947
|
+
const queryRequest = {
|
948
|
+
params: { entityId, instanceId, roomType },
|
949
|
+
skip,
|
950
|
+
take,
|
919
951
|
};
|
952
|
+
return this.query(queryRequest);
|
920
953
|
}
|
921
|
-
|
922
|
-
|
923
|
-
const currentUser = this.getCurrentUser();
|
924
|
-
const hasCurrentUser = memberIds.includes(currentUser.id);
|
925
|
-
const allMemberIds = hasCurrentUser ? memberIds : [...memberIds, currentUser.id];
|
926
|
-
try {
|
927
|
-
// Get user info for all members
|
928
|
-
const memberReferences = await Promise.all(allMemberIds.map((id) => this.getUserInfo(id)));
|
929
|
-
// Get user names for title
|
930
|
-
const userNames = await Promise.all(memberReferences.map(async (ref) => {
|
931
|
-
try {
|
932
|
-
const user = await this.usersService.getOne(ref.id);
|
933
|
-
return `${user.firstName} ${user.lastName}`.trim();
|
934
|
-
}
|
935
|
-
catch (error) {
|
936
|
-
return ref.id;
|
937
|
-
}
|
938
|
-
}));
|
939
|
-
// Create a new room - this returns just the ID as a string
|
940
|
-
const roomId = await this.createRoom({
|
941
|
-
members: memberReferences,
|
942
|
-
title: userNames.join(', '),
|
943
|
-
});
|
944
|
-
// Get the full room data with the ID
|
945
|
-
return this.getChatRoom(roomId);
|
946
|
-
}
|
947
|
-
catch (error) {
|
948
|
-
console.error('Failed to create conversation:', error);
|
949
|
-
throw error;
|
950
|
-
}
|
951
|
-
}
|
952
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
953
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, providedIn: 'root' }); }
|
954
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
955
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, providedIn: 'root' }); }
|
954
956
|
}
|
955
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
957
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, decorators: [{
|
956
958
|
type: Injectable,
|
957
959
|
args: [{
|
958
960
|
providedIn: 'root',
|
959
961
|
}]
|
960
962
|
}] });
|
961
963
|
|
962
|
-
class
|
964
|
+
class AXMCommentLookupPopup extends AXBasePageComponent {
|
963
965
|
constructor() {
|
964
|
-
|
965
|
-
this.
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
return initials.join('');
|
966
|
+
super(...arguments);
|
967
|
+
this.lookupNode = {
|
968
|
+
name: 'form-field',
|
969
|
+
path: 'form-field',
|
970
|
+
type: 'form-field',
|
971
|
+
children: [
|
972
|
+
{
|
973
|
+
name: 'lookup',
|
974
|
+
path: 'lookup',
|
975
|
+
type: 'lookup-editor',
|
976
|
+
options: {
|
977
|
+
entity: 'SecurityManagement.users',
|
978
|
+
multiple: true,
|
979
|
+
},
|
980
|
+
},
|
981
|
+
],
|
982
|
+
options: {
|
983
|
+
label: 'Select users:',
|
984
|
+
},
|
985
|
+
};
|
986
|
+
this.titleNode = {
|
987
|
+
name: 'form-field',
|
988
|
+
path: 'form-field',
|
989
|
+
type: 'form-field',
|
990
|
+
children: [
|
991
|
+
{
|
992
|
+
name: 'title',
|
993
|
+
path: 'title',
|
994
|
+
type: 'text-editor',
|
995
|
+
options: {
|
996
|
+
placeholder: 'Enter title',
|
997
|
+
required: true,
|
998
|
+
},
|
999
|
+
},
|
1000
|
+
],
|
1001
|
+
options: {
|
1002
|
+
label: 'Title:',
|
1003
|
+
},
|
1004
|
+
};
|
1005
|
+
this.context = signal({});
|
1005
1006
|
}
|
1006
|
-
|
1007
|
-
this.
|
1007
|
+
handleClose() {
|
1008
|
+
this.close({
|
1009
|
+
lookup: this.context()?.lookup,
|
1010
|
+
title: this.context()?.title,
|
1011
|
+
});
|
1008
1012
|
}
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
// Check if message has been seen by anyone
|
1016
|
-
return lastMessage.hasSeen ? 'ax-icon-dobble-check' : 'ax-icon-check';
|
1013
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentLookupPopup, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
1014
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentLookupPopup, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: `<axp-widgets-container [context]="context()" (onContextChanged)="context.set($event.data)">
|
1015
|
+
<div class="ax-p-4 ax-flex ax-flex-col ax-gap-4">
|
1016
|
+
<ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
|
1017
|
+
@if (context()?.lookup?.length > 1) {
|
1018
|
+
<ng-container axp-widget-renderer [node]="titleNode" [mode]="'edit'"> </ng-container>
|
1017
1019
|
}
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1020
|
+
</div>
|
1021
|
+
</axp-widgets-container>
|
1022
|
+
|
1023
|
+
<ax-footer>
|
1024
|
+
<ax-suffix>
|
1025
|
+
<ax-button
|
1026
|
+
text="Accept & Send"
|
1027
|
+
color="primary"
|
1028
|
+
[disabled]="context()?.lookup?.length > 1 && !context()?.title"
|
1029
|
+
(onClick)="handleClose()"
|
1030
|
+
></ax-button>
|
1031
|
+
</ax-suffix>
|
1032
|
+
</ax-footer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXPLayoutBuilderModule }, { kind: "component", type: i2.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i2.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }] }); }
|
1022
1033
|
}
|
1023
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
1034
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentLookupPopup, decorators: [{
|
1024
1035
|
type: Component,
|
1025
|
-
args: [{
|
1036
|
+
args: [{
|
1037
|
+
template: `<axp-widgets-container [context]="context()" (onContextChanged)="context.set($event.data)">
|
1038
|
+
<div class="ax-p-4 ax-flex ax-flex-col ax-gap-4">
|
1039
|
+
<ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
|
1040
|
+
@if (context()?.lookup?.length > 1) {
|
1041
|
+
<ng-container axp-widget-renderer [node]="titleNode" [mode]="'edit'"> </ng-container>
|
1042
|
+
}
|
1043
|
+
</div>
|
1044
|
+
</axp-widgets-container>
|
1045
|
+
|
1046
|
+
<ax-footer>
|
1047
|
+
<ax-suffix>
|
1048
|
+
<ax-button
|
1049
|
+
text="Accept & Send"
|
1050
|
+
color="primary"
|
1051
|
+
[disabled]="context()?.lookup?.length > 1 && !context()?.title"
|
1052
|
+
(onClick)="handleClose()"
|
1053
|
+
></ax-button>
|
1054
|
+
</ax-suffix>
|
1055
|
+
</ax-footer>`,
|
1056
|
+
imports: [AXDecoratorModule, AXPLayoutBuilderModule, AXButtonModule],
|
1057
|
+
}]
|
1026
1058
|
}] });
|
1027
1059
|
|
1028
|
-
class
|
1060
|
+
class AXMCommentListViewComponent {
|
1029
1061
|
constructor() {
|
1062
|
+
this.hasCooldown = signal(false);
|
1063
|
+
this.commentContent = signal('');
|
1064
|
+
this.isSubmitting = signal(false);
|
1065
|
+
this.isReplyingMode = signal(false);
|
1066
|
+
this.isEditingMode = signal(false);
|
1067
|
+
this.isLoading = signal(true);
|
1068
|
+
this.failedImageIds = signal([]);
|
1069
|
+
this.activeReplyComment = signal(undefined);
|
1070
|
+
this.activeEditComment = signal(undefined);
|
1071
|
+
this.wysiwygEditor = viewChild.required('w');
|
1072
|
+
this.commentService = inject(AXMCommentService);
|
1073
|
+
this.platform = inject(AXPlatform);
|
1030
1074
|
this.route = inject(ActivatedRoute);
|
1031
|
-
this.
|
1032
|
-
this.
|
1033
|
-
|
1034
|
-
this.
|
1035
|
-
|
1036
|
-
this.
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
this.
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
this.
|
1048
|
-
|
1049
|
-
|
1050
|
-
if (this.searchQuery()) {
|
1051
|
-
const searchText = this.searchQuery().toLowerCase();
|
1052
|
-
chatRooms = chatRooms.filter((room) => {
|
1053
|
-
const roomName = room.roomMembers?.[0]?.fullName || '';
|
1054
|
-
const lastMessage = room.lastMessage?.message?.content || '';
|
1055
|
-
return roomName.toLowerCase().includes(searchText) || lastMessage.toLowerCase().includes(searchText);
|
1056
|
-
});
|
1057
|
-
}
|
1058
|
-
// Apply tab filter
|
1059
|
-
if (this.activeTab() === 1) {
|
1060
|
-
return chatRooms.filter((room) => room.unreadCount > 0);
|
1061
|
-
}
|
1062
|
-
return chatRooms;
|
1075
|
+
this.popupService = inject(AXPopupService);
|
1076
|
+
this.toastService = inject(AXToastService);
|
1077
|
+
this.dialogService = inject(AXDialogService);
|
1078
|
+
this.sanitize = inject(DomSanitizer);
|
1079
|
+
this.routeParams = this.route.snapshot;
|
1080
|
+
this.getPayload = computed(() => ({
|
1081
|
+
params: this.payload(),
|
1082
|
+
skip: 0,
|
1083
|
+
take: 10,
|
1084
|
+
}));
|
1085
|
+
this.payload = computed(() => ({
|
1086
|
+
roomType: 'default',
|
1087
|
+
entityId: this.routeParams.params?.['module'] + '.' + this.routeParams.params?.['entity'],
|
1088
|
+
instanceId: this.routeParams.params?.['id'],
|
1089
|
+
}));
|
1090
|
+
this.comments = signal([]);
|
1091
|
+
this.wysiwyg = viewChild('w');
|
1092
|
+
this.wysiwygOptions = signal({
|
1093
|
+
look: 'solid',
|
1063
1094
|
});
|
1064
|
-
this.
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
afterNextRender(() => {
|
1069
|
-
const tabComponent = this.tab();
|
1070
|
-
if (tabComponent) {
|
1071
|
-
tabComponent.onActiveTabChanged.subscribe((i) => {
|
1072
|
-
this.activeTab.set(i.index);
|
1073
|
-
});
|
1074
|
-
}
|
1095
|
+
this.avatarConfig = signal({
|
1096
|
+
color: 'primary',
|
1097
|
+
look: 'rounded',
|
1098
|
+
type: 'solid', // 'image' | 'text' | 'icon' | 'default'
|
1075
1099
|
});
|
1100
|
+
this.validateContent = (content) => {
|
1101
|
+
let isValid = true;
|
1102
|
+
if (!content || content === '<p><br></p>') {
|
1103
|
+
isValid = false;
|
1104
|
+
}
|
1105
|
+
return {
|
1106
|
+
rule: 'callback',
|
1107
|
+
result: isValid,
|
1108
|
+
message: isValid ? '' : 'Please fill the content',
|
1109
|
+
value: content,
|
1110
|
+
};
|
1111
|
+
};
|
1076
1112
|
}
|
1077
|
-
|
1078
|
-
|
1113
|
+
ngOnInit() {
|
1114
|
+
this.loadComments();
|
1079
1115
|
}
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
: 'Select A Chat First';
|
1116
|
+
sanitizeHtml(htmlContent) {
|
1117
|
+
if (!htmlContent)
|
1118
|
+
return this.sanitize.bypassSecurityTrustHtml('');
|
1119
|
+
return this.sanitize.bypassSecurityTrustHtml(htmlContent);
|
1085
1120
|
}
|
1086
|
-
|
1087
|
-
|
1121
|
+
handleImageError(imageId) {
|
1122
|
+
this.failedImageIds.update((ids) => [...ids, imageId]);
|
1088
1123
|
}
|
1089
|
-
|
1090
|
-
return
|
1124
|
+
checkImageExists(imageId) {
|
1125
|
+
return !this.failedImageIds().includes(imageId);
|
1091
1126
|
}
|
1092
|
-
|
1093
|
-
|
1127
|
+
extractInitials(name) {
|
1128
|
+
if (!name)
|
1129
|
+
return '?';
|
1130
|
+
// Handle the case where name is an object with fullName property
|
1131
|
+
const nameStr = typeof name === 'object' && name.fullName ? name.fullName : String(name);
|
1132
|
+
const words = nameStr.split(' ');
|
1133
|
+
const initials = words.map((word) => word.charAt(0).toUpperCase());
|
1134
|
+
return initials.join('');
|
1135
|
+
}
|
1136
|
+
async loadComments() {
|
1137
|
+
this.isLoading.set(true);
|
1094
1138
|
try {
|
1095
|
-
this.
|
1096
|
-
this.
|
1097
|
-
// Use the updated service method to get rooms with last messages
|
1098
|
-
const response = await this.chatService.getAllChatRooms();
|
1099
|
-
this.allRooms.set(response.items);
|
1100
|
-
this.rooms.set(response.items);
|
1101
|
-
this.isLoading.set(false);
|
1139
|
+
const response = await this.commentService.query(this.getPayload());
|
1140
|
+
this.comments.set(response.items);
|
1102
1141
|
}
|
1103
1142
|
catch (error) {
|
1104
|
-
|
1105
|
-
this.
|
1106
|
-
|
1143
|
+
console.error('Failed to load comments:', error);
|
1144
|
+
this.toastService.show({
|
1145
|
+
content: 'Failed to load comments. Please try again.',
|
1146
|
+
color: 'danger',
|
1147
|
+
location: 'bottom-center',
|
1148
|
+
closeButton: true,
|
1149
|
+
timeOut: 3000,
|
1150
|
+
timeOutProgress: true,
|
1151
|
+
});
|
1152
|
+
this.comments.set([]);
|
1153
|
+
}
|
1154
|
+
finally {
|
1155
|
+
setTimeout(() => {
|
1156
|
+
this.isLoading.set(false);
|
1157
|
+
}, 250);
|
1107
1158
|
}
|
1108
1159
|
}
|
1109
|
-
|
1110
|
-
|
1160
|
+
editMessage(comment, reply) {
|
1161
|
+
this.isReplyingMode.set(false);
|
1162
|
+
this.activeReplyComment.set(undefined);
|
1163
|
+
this.isEditingMode.set(true);
|
1164
|
+
this.activeEditComment.set(comment);
|
1165
|
+
const contentToEdit = reply ? reply.message?.content : comment.message?.content;
|
1166
|
+
this.commentContent.set(contentToEdit || '');
|
1167
|
+
this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
|
1111
1168
|
}
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
const
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
return room;
|
1124
|
-
});
|
1125
|
-
// Update the rooms signals
|
1126
|
-
this.allRooms.set(updatedRooms);
|
1127
|
-
this.rooms.set(updatedRooms);
|
1128
|
-
// Find the selected room
|
1129
|
-
const selectedRoom = updatedRooms.find((room) => room.id === chatId) || null;
|
1130
|
-
this.selectedRoom.set(selectedRoom);
|
1131
|
-
// Navigate to the chat details
|
1132
|
-
this.router.navigate([chatId], { relativeTo: this.route });
|
1133
|
-
}
|
1134
|
-
catch (error) {
|
1135
|
-
console.error('Failed to mark chat as read:', error);
|
1169
|
+
replyMessage(comment, reply) {
|
1170
|
+
this.isEditingMode.set(false);
|
1171
|
+
this.activeEditComment.set(undefined);
|
1172
|
+
this.isReplyingMode.set(true);
|
1173
|
+
this.activeReplyComment.set(comment);
|
1174
|
+
if (reply) {
|
1175
|
+
const author = reply.author.fullName || reply.author.id;
|
1176
|
+
const mention = `<a data-id="${reply.id}">@${author}</a> `;
|
1177
|
+
this.commentContent.set(mention);
|
1178
|
+
this.wysiwyg()?.focus();
|
1179
|
+
document.getElementsByClassName('ql-editor')[0].innerHTML = mention;
|
1136
1180
|
}
|
1181
|
+
this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
|
1137
1182
|
}
|
1138
|
-
|
1139
|
-
this.
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1183
|
+
async deleteComment(comment) {
|
1184
|
+
const dialog = this.dialogService.open({
|
1185
|
+
icon: 'fa-regular fa-warning',
|
1186
|
+
content: 'Are you sure you want to delete this comment?',
|
1187
|
+
title: 'Delete Comment',
|
1188
|
+
type: 'danger',
|
1189
|
+
orientation: 'horizontal',
|
1190
|
+
buttons: [
|
1191
|
+
{
|
1192
|
+
text: 'Delete',
|
1193
|
+
color: 'danger',
|
1194
|
+
onClick: async (e) => {
|
1195
|
+
e.handled = true;
|
1196
|
+
e.source.text = 'Deleting...';
|
1197
|
+
e.source.disabled = true;
|
1198
|
+
e.source.loading = true;
|
1199
|
+
try {
|
1200
|
+
if (comment.id) {
|
1201
|
+
await this.commentService.deleteOne(comment.id);
|
1202
|
+
this.removeMessageById(comment.id);
|
1203
|
+
this.toastService.show({
|
1204
|
+
content: 'Comment deleted successfully.',
|
1205
|
+
color: 'success',
|
1206
|
+
location: 'bottom-center',
|
1207
|
+
closeButton: true,
|
1208
|
+
timeOut: 3000,
|
1209
|
+
timeOutProgress: true,
|
1210
|
+
});
|
1211
|
+
if (this.isEditingMode() && this.activeEditComment()?.id === comment.id) {
|
1212
|
+
this.resetReplyEditState();
|
1213
|
+
}
|
1214
|
+
dialog.close();
|
1215
|
+
}
|
1216
|
+
}
|
1217
|
+
catch (error) {
|
1218
|
+
this.toastService.show({
|
1219
|
+
content: typeof error === 'string' ? error : 'Failed to delete comment!',
|
1220
|
+
color: 'danger',
|
1221
|
+
location: 'bottom-center',
|
1222
|
+
closeButton: true,
|
1223
|
+
timeOut: 3000,
|
1224
|
+
timeOutProgress: true,
|
1225
|
+
});
|
1226
|
+
}
|
1227
|
+
},
|
1228
|
+
},
|
1229
|
+
{
|
1230
|
+
text: 'Cancel',
|
1231
|
+
color: 'default',
|
1232
|
+
autofocus: true,
|
1233
|
+
onClick: (e) => {
|
1234
|
+
dialog.close();
|
1235
|
+
},
|
1236
|
+
},
|
1237
|
+
],
|
1238
|
+
closeButton: false,
|
1239
|
+
});
|
1158
1240
|
}
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1241
|
+
async deleteReply(comment, reply) {
|
1242
|
+
const dialog = this.dialogService.open({
|
1243
|
+
icon: 'fa-regular fa-warning',
|
1244
|
+
content: 'Are you sure you want to delete this reply?',
|
1245
|
+
title: 'Delete Reply',
|
1246
|
+
type: 'danger',
|
1247
|
+
orientation: 'horizontal',
|
1248
|
+
buttons: [
|
1249
|
+
{
|
1250
|
+
text: 'Delete',
|
1251
|
+
color: 'danger',
|
1252
|
+
onClick: async (e) => {
|
1253
|
+
e.handled = true;
|
1254
|
+
e.source.text = 'Deleting...';
|
1255
|
+
e.source.disabled = true;
|
1256
|
+
e.source.loading = true;
|
1257
|
+
try {
|
1258
|
+
if (reply.id) {
|
1259
|
+
await this.commentService.deleteOne(reply.id);
|
1260
|
+
this.removeMessageById(comment.id, reply.id);
|
1261
|
+
this.toastService.show({
|
1262
|
+
content: 'Comment deleted successfully.',
|
1263
|
+
color: 'success',
|
1264
|
+
location: 'bottom-center',
|
1265
|
+
closeButton: true,
|
1266
|
+
timeOut: 3000,
|
1267
|
+
timeOutProgress: true,
|
1268
|
+
});
|
1269
|
+
dialog.close();
|
1270
|
+
}
|
1271
|
+
}
|
1272
|
+
catch (error) {
|
1273
|
+
this.toastService.show({
|
1274
|
+
content: typeof error === 'string' ? error : 'Failed to delete comment!',
|
1275
|
+
color: 'danger',
|
1276
|
+
location: 'bottom-center',
|
1277
|
+
closeButton: true,
|
1278
|
+
timeOut: 3000,
|
1279
|
+
timeOutProgress: true,
|
1280
|
+
});
|
1281
|
+
}
|
1282
|
+
},
|
1283
|
+
},
|
1284
|
+
{
|
1285
|
+
text: 'Cancel',
|
1286
|
+
color: 'default',
|
1287
|
+
autofocus: true,
|
1288
|
+
onClick: (e) => {
|
1289
|
+
dialog.close();
|
1290
|
+
},
|
1291
|
+
},
|
1292
|
+
],
|
1293
|
+
closeButton: false,
|
1294
|
+
});
|
1162
1295
|
}
|
1163
|
-
|
1164
|
-
|
1296
|
+
resetReplyEditState() {
|
1297
|
+
this.isEditingMode.set(false);
|
1298
|
+
this.activeEditComment.set(undefined);
|
1299
|
+
this.isReplyingMode.set(false);
|
1300
|
+
this.activeReplyComment.set(undefined);
|
1301
|
+
this.commentContent.set('');
|
1302
|
+
document.getElementsByClassName('ql-editor')[0].innerHTML = '';
|
1165
1303
|
}
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1304
|
+
async toggleLike(comment, reply) {
|
1305
|
+
try {
|
1306
|
+
if (reply) {
|
1307
|
+
if (reply.id) {
|
1308
|
+
this.updateLikeStatus(comment.id, reply.id);
|
1309
|
+
await this.commentService.like(reply.id);
|
1310
|
+
}
|
1311
|
+
}
|
1312
|
+
else {
|
1313
|
+
if (comment.id) {
|
1314
|
+
this.updateLikeStatus(comment.id);
|
1315
|
+
await this.commentService.like(comment.id);
|
1316
|
+
}
|
1317
|
+
}
|
1318
|
+
}
|
1319
|
+
catch (error) {
|
1320
|
+
console.error('Failed to toggle like:', error);
|
1321
|
+
// Revert the optimistic update if the server call fails
|
1322
|
+
if (reply && reply.id) {
|
1323
|
+
this.updateLikeStatus(comment.id, reply.id);
|
1324
|
+
}
|
1325
|
+
else if (comment.id) {
|
1326
|
+
this.updateLikeStatus(comment.id);
|
1327
|
+
}
|
1328
|
+
}
|
1182
1329
|
}
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
break;
|
1202
|
-
case 'link':
|
1203
|
-
// Determine if link is for audio or other content
|
1204
|
-
const content = message.message.content || '';
|
1205
|
-
if (content.includes('.mp3') || content.includes('.ogg') || content.includes('audio')) {
|
1206
|
-
messageType = 'audio';
|
1330
|
+
updateLikeStatus(commentId, replyId) {
|
1331
|
+
this.comments.update((commentsList) => {
|
1332
|
+
return commentsList.map((comment) => {
|
1333
|
+
if (comment.id === commentId) {
|
1334
|
+
if (replyId && comment.replies) {
|
1335
|
+
const updatedReplies = comment.replies.map((reply) => reply.id === replyId
|
1336
|
+
? {
|
1337
|
+
...reply,
|
1338
|
+
isLiked: !reply.isLiked,
|
1339
|
+
reactionsCount: reply.isLiked && reply.reactionsCount !== undefined
|
1340
|
+
? reply.reactionsCount - 1
|
1341
|
+
: (reply.reactionsCount || 0) + 1,
|
1342
|
+
}
|
1343
|
+
: reply);
|
1344
|
+
return {
|
1345
|
+
...comment,
|
1346
|
+
replies: updatedReplies,
|
1347
|
+
};
|
1207
1348
|
}
|
1208
1349
|
else {
|
1209
|
-
|
1350
|
+
return {
|
1351
|
+
...comment,
|
1352
|
+
isLiked: !comment.isLiked,
|
1353
|
+
reactionsCount: comment.isLiked && comment.reactionsCount !== undefined
|
1354
|
+
? comment.reactionsCount - 1
|
1355
|
+
: (comment.reactionsCount || 0) + 1,
|
1356
|
+
};
|
1210
1357
|
}
|
1211
|
-
|
1212
|
-
|
1213
|
-
messageType = 'text';
|
1214
|
-
}
|
1215
|
-
}
|
1216
|
-
// Extract author name if available
|
1217
|
-
const name = message.author ? message.author.id : 'Unknown';
|
1218
|
-
// Create the chat preview message
|
1219
|
-
const previewMessage = {
|
1220
|
-
id: message.id,
|
1221
|
-
sendTime: new Date(message.createdAt || new Date()),
|
1222
|
-
readTime: new Date(), // Assuming "read" when converted
|
1223
|
-
type: messageType,
|
1224
|
-
content: message.message?.content || '',
|
1225
|
-
name: name,
|
1226
|
-
};
|
1227
|
-
// Add fromId if the message is not from current user
|
1228
|
-
if (!message.isFromCurrentUser) {
|
1229
|
-
previewMessage.fromId = message.author?.id;
|
1230
|
-
}
|
1231
|
-
// Note: Reply information would need to be looked up from the actual messages
|
1232
|
-
// This would typically be done by the service that has access to all messages
|
1233
|
-
return previewMessage;
|
1234
|
-
});
|
1235
|
-
}
|
1236
|
-
/**
|
1237
|
-
* Creates a ChatPreviewMessage from reply information
|
1238
|
-
* @param message - The original message that is being referenced
|
1239
|
-
* @returns A ChatPreviewMessage formatted for use as a replyTo
|
1240
|
-
*/
|
1241
|
-
function createReplyPreview(message) {
|
1242
|
-
if (!message)
|
1243
|
-
return undefined;
|
1244
|
-
return {
|
1245
|
-
id: message.id,
|
1246
|
-
sendTime: new Date(message.createdAt || new Date()),
|
1247
|
-
readTime: new Date(),
|
1248
|
-
type: message.message?.contentType === 'image'
|
1249
|
-
? 'image'
|
1250
|
-
: message.message?.contentType === 'video'
|
1251
|
-
? 'video'
|
1252
|
-
: message.message?.contentType === 'file'
|
1253
|
-
? 'file'
|
1254
|
-
: 'text',
|
1255
|
-
content: message.message?.content || '',
|
1256
|
-
name: message.author?.id || 'Unknown',
|
1257
|
-
fromId: !message.isFromCurrentUser ? message.author?.id : undefined,
|
1258
|
-
};
|
1259
|
-
}
|
1260
|
-
/**
|
1261
|
-
* Generate demo chat preview data with a variety of message types
|
1262
|
-
* @returns Array of sample chat preview messages
|
1263
|
-
*/
|
1264
|
-
function generateDemoChatPreview() {
|
1265
|
-
return [
|
1266
|
-
{
|
1267
|
-
id: '0',
|
1268
|
-
sendTime: new Date(),
|
1269
|
-
type: 'text',
|
1270
|
-
readTime: new Date(),
|
1271
|
-
content: 'Hello John, How are you?',
|
1272
|
-
name: 'test name',
|
1273
|
-
},
|
1274
|
-
{
|
1275
|
-
id: '1',
|
1276
|
-
fromId: '10',
|
1277
|
-
sendTime: new Date(),
|
1278
|
-
readTime: new Date(),
|
1279
|
-
type: 'text',
|
1280
|
-
content: 'Hello',
|
1281
|
-
name: 'test name',
|
1282
|
-
replyTo: {
|
1283
|
-
id: '0',
|
1284
|
-
sendTime: new Date(),
|
1285
|
-
type: 'text',
|
1286
|
-
readTime: new Date(),
|
1287
|
-
content: 'Hello John, How are you?',
|
1288
|
-
name: 'test name',
|
1289
|
-
},
|
1290
|
-
},
|
1291
|
-
{
|
1292
|
-
id: '2',
|
1293
|
-
fromId: '10',
|
1294
|
-
sendTime: new Date(),
|
1295
|
-
readTime: new Date(),
|
1296
|
-
type: 'voice',
|
1297
|
-
name: 'test name',
|
1298
|
-
content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
|
1299
|
-
},
|
1300
|
-
{
|
1301
|
-
id: '3',
|
1302
|
-
sendTime: new Date(),
|
1303
|
-
readTime: new Date(),
|
1304
|
-
type: 'text',
|
1305
|
-
content: 'Can i have your address information?',
|
1306
|
-
name: 'test name',
|
1307
|
-
replyTo: {
|
1308
|
-
id: '1',
|
1309
|
-
fromId: '10',
|
1310
|
-
sendTime: new Date(),
|
1311
|
-
readTime: new Date(),
|
1312
|
-
type: 'text',
|
1313
|
-
content: 'Hello',
|
1314
|
-
name: 'test name',
|
1315
|
-
},
|
1316
|
-
},
|
1317
|
-
{
|
1318
|
-
id: '4',
|
1319
|
-
fromId: '10',
|
1320
|
-
sendTime: new Date(),
|
1321
|
-
readTime: new Date(),
|
1322
|
-
type: 'file',
|
1323
|
-
name: 'test name',
|
1324
|
-
content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
|
1325
|
-
},
|
1326
|
-
{
|
1327
|
-
id: '5',
|
1328
|
-
sendTime: new Date(),
|
1329
|
-
readTime: new Date(),
|
1330
|
-
type: 'image',
|
1331
|
-
name: 'test name',
|
1332
|
-
content: `https://picsum.photos/300/200`,
|
1333
|
-
},
|
1334
|
-
{
|
1335
|
-
id: '6',
|
1336
|
-
sendTime: new Date(),
|
1337
|
-
readTime: new Date(),
|
1338
|
-
type: 'video',
|
1339
|
-
name: 'test name',
|
1340
|
-
content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
|
1341
|
-
},
|
1342
|
-
{
|
1343
|
-
id: '7',
|
1344
|
-
sendTime: new Date(),
|
1345
|
-
type: 'text',
|
1346
|
-
readTime: new Date(),
|
1347
|
-
content: 'Hello John, How are you?',
|
1348
|
-
name: 'test name',
|
1349
|
-
},
|
1350
|
-
{
|
1351
|
-
id: '8',
|
1352
|
-
fromId: '10',
|
1353
|
-
sendTime: new Date(),
|
1354
|
-
readTime: new Date(),
|
1355
|
-
type: 'audio',
|
1356
|
-
name: 'kids',
|
1357
|
-
content: `https://actions.google.com/sounds/v1/ambiences/kids_playing.ogg`,
|
1358
|
-
},
|
1359
|
-
{
|
1360
|
-
id: '9',
|
1361
|
-
fromId: '10',
|
1362
|
-
sendTime: new Date(),
|
1363
|
-
readTime: new Date(),
|
1364
|
-
type: 'image',
|
1365
|
-
name: 'test name',
|
1366
|
-
content: `https://picsum.photos/300/200`,
|
1367
|
-
},
|
1368
|
-
{
|
1369
|
-
id: '10',
|
1370
|
-
fromId: '10',
|
1371
|
-
sendTime: new Date(),
|
1372
|
-
readTime: new Date(),
|
1373
|
-
type: 'image',
|
1374
|
-
name: 'test name',
|
1375
|
-
content: `https://picsum.photos/200/300`,
|
1376
|
-
},
|
1377
|
-
{
|
1378
|
-
id: '11',
|
1379
|
-
fromId: '10',
|
1380
|
-
sendTime: new Date(),
|
1381
|
-
readTime: new Date(),
|
1382
|
-
type: 'video',
|
1383
|
-
name: 'test name',
|
1384
|
-
content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
|
1385
|
-
},
|
1386
|
-
{
|
1387
|
-
id: '12',
|
1388
|
-
sendTime: new Date(),
|
1389
|
-
readTime: new Date(),
|
1390
|
-
type: 'file',
|
1391
|
-
name: 'test name',
|
1392
|
-
content: `data:audio/webm;codecs=opus;base64,GkXfo59ChoE+u5BxHVL7ZAS1EF34fJ`,
|
1393
|
-
},
|
1394
|
-
{
|
1395
|
-
id: '13',
|
1396
|
-
sendTime: new Date(),
|
1397
|
-
readTime: new Date(),
|
1398
|
-
type: 'audio',
|
1399
|
-
name: 'alarm',
|
1400
|
-
content: `https://actions.google.com/sounds/v1/alarms/digital_watch_alarm_long.ogg`,
|
1401
|
-
},
|
1402
|
-
];
|
1403
|
-
}
|
1404
|
-
/**
|
1405
|
-
* Usage Examples:
|
1406
|
-
*
|
1407
|
-
* Example 1: Converting chat service response to chat preview format
|
1408
|
-
* ```typescript
|
1409
|
-
* import { AXMChatService } from '../chat.service';
|
1410
|
-
* import { convertToChatPreview } from './chat-preview.helper';
|
1411
|
-
*
|
1412
|
-
* export class ChatPreviewComponent {
|
1413
|
-
* constructor(private chatService: AXMChatService) {}
|
1414
|
-
*
|
1415
|
-
* async loadChatMessages(roomId: string) {
|
1416
|
-
* const response = await this.chatService.getChatMessages(roomId);
|
1417
|
-
*
|
1418
|
-
* // Convert to chat preview format
|
1419
|
-
* const previewMessages = convertToChatPreview(response);
|
1420
|
-
*
|
1421
|
-
* // Use the converted messages
|
1422
|
-
* this.conversationService.chats.set(previewMessages);
|
1423
|
-
* }
|
1424
|
-
* }
|
1425
|
-
* ```
|
1426
|
-
*
|
1427
|
-
* Example 2: Using demo data for development/testing
|
1428
|
-
* ```typescript
|
1429
|
-
* import { generateDemoChatPreview } from './chat-preview.helper';
|
1430
|
-
*
|
1431
|
-
* export class ChatPreviewDemoComponent {
|
1432
|
-
* ngOnInit() {
|
1433
|
-
* // Load demo messages
|
1434
|
-
* const demoMessages = generateDemoChatPreview();
|
1435
|
-
*
|
1436
|
-
* // Use the demo messages
|
1437
|
-
* this.conversationService.chats.set(demoMessages);
|
1438
|
-
* }
|
1439
|
-
* }
|
1440
|
-
*/
|
1441
|
-
|
1442
|
-
class AXMChatPreviewComponent {
|
1443
|
-
constructor(elRef) {
|
1444
|
-
this.elRef = elRef;
|
1445
|
-
// Signal for dynamic max-height (using computed logic for real-time updates)
|
1446
|
-
this.activatedRoute = inject(ActivatedRoute);
|
1447
|
-
this.fileService = inject(AXFileService);
|
1448
|
-
// private conversationService = inject(AXConversationService);
|
1449
|
-
this.chatService = inject(AXMChatService);
|
1450
|
-
this.options = signal({
|
1451
|
-
disabled: false,
|
1452
|
-
readonly: false,
|
1453
|
-
value: '',
|
1454
|
-
});
|
1455
|
-
this.conversationViewMaxHeight = signal('');
|
1456
|
-
this.initialTextAreaHeight = 0;
|
1457
|
-
this.textareaHeight = signal(0);
|
1458
|
-
this.dynamicHeight = computed(() => {
|
1459
|
-
const baseOffset = 275;
|
1460
|
-
const currentHeight = this.textareaHeight();
|
1461
|
-
const dynamicOffset = baseOffset + (currentHeight - this.initialTextAreaHeight);
|
1462
|
-
return `calc(100vh - ${dynamicOffset}px) !important`;
|
1463
|
-
});
|
1464
|
-
afterNextRender(() => {
|
1465
|
-
const textareaContainer = this.elRef.nativeElement.querySelector('ax-conversation-input > div');
|
1466
|
-
if (textareaContainer) {
|
1467
|
-
this.initialTextAreaHeight = textareaContainer.offsetHeight;
|
1468
|
-
this.resizeObserver = new ResizeObserver((entries) => {
|
1469
|
-
for (const entry of entries) {
|
1470
|
-
if (entry.target === textareaContainer) {
|
1471
|
-
const currentHeight = entry.contentRect.height;
|
1472
|
-
this.textareaHeight.set(currentHeight);
|
1473
|
-
}
|
1474
|
-
}
|
1475
|
-
});
|
1476
|
-
this.resizeObserver.observe(textareaContainer);
|
1477
|
-
}
|
1478
|
-
else {
|
1479
|
-
console.warn('Textarea Container element not found.');
|
1480
|
-
}
|
1481
|
-
this.activatedRoute.params.subscribe((params) => {
|
1482
|
-
console.log(params['id']);
|
1483
|
-
this.messageId = params['id'];
|
1484
|
-
this.loadMessages(this.messageId);
|
1358
|
+
}
|
1359
|
+
return comment;
|
1485
1360
|
});
|
1486
1361
|
});
|
1487
1362
|
}
|
1488
|
-
|
1489
|
-
if (
|
1490
|
-
|
1363
|
+
removeMessageById(commentId, replyId) {
|
1364
|
+
if (replyId) {
|
1365
|
+
// Remove a reply from a comment
|
1366
|
+
this.comments.update((commentsList) => commentsList.map((comment) => {
|
1367
|
+
if (comment.id === commentId && comment.replies) {
|
1368
|
+
return {
|
1369
|
+
...comment,
|
1370
|
+
replies: comment.replies.filter((reply) => reply.id !== replyId),
|
1371
|
+
};
|
1372
|
+
}
|
1373
|
+
return comment;
|
1374
|
+
}));
|
1375
|
+
}
|
1376
|
+
else {
|
1377
|
+
// Remove a whole comment
|
1378
|
+
this.comments.update((commentsList) => commentsList.filter((comment) => comment.id !== commentId));
|
1491
1379
|
}
|
1492
1380
|
}
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
type: 'voice',
|
1508
|
-
name: 'test name',
|
1509
|
-
};
|
1510
|
-
// this.conversationService.chats.update((values: any) => [...values, newVoiceMessage]);
|
1511
|
-
});
|
1512
|
-
}
|
1513
|
-
handleOnSend(e) {
|
1514
|
-
console.log('Message Sent:', e);
|
1515
|
-
if (e.data.value) {
|
1516
|
-
this.options.update((prev) => ({ ...prev, value: '' }));
|
1517
|
-
// Create a properly formatted chat preview message
|
1518
|
-
const newMessage = {
|
1519
|
-
id: `${Math.floor(Math.random() * 10000)}`,
|
1520
|
-
content: e.data.value,
|
1521
|
-
sendTime: new Date(),
|
1522
|
-
readTime: new Date(),
|
1523
|
-
type: e.data.type || 'text',
|
1524
|
-
name: 'test name',
|
1525
|
-
replyTo: e.data.replyChat,
|
1381
|
+
async submitComment(isPrivate = false) {
|
1382
|
+
if (!this.validateContent(this.commentContent()).result) {
|
1383
|
+
return;
|
1384
|
+
}
|
1385
|
+
this.isSubmitting.set(true);
|
1386
|
+
let memberLookup;
|
1387
|
+
if (isPrivate) {
|
1388
|
+
const popupConfig = {
|
1389
|
+
header: true,
|
1390
|
+
size: 'md',
|
1391
|
+
draggable: true,
|
1392
|
+
hasBackdrop: true,
|
1393
|
+
closeButton: true,
|
1394
|
+
closeOnBackdropClick: false,
|
1526
1395
|
};
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1396
|
+
const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
|
1397
|
+
memberLookup = popup.data?.lookup;
|
1398
|
+
}
|
1399
|
+
try {
|
1400
|
+
if (this.isEditingMode() && this.activeEditComment()?.id) {
|
1401
|
+
const payload = {
|
1402
|
+
message: {
|
1403
|
+
content: this.commentContent(),
|
1404
|
+
contentType: 'text',
|
1405
|
+
},
|
1406
|
+
};
|
1407
|
+
await this.commentService.updateOne(this.activeEditComment().id, payload);
|
1408
|
+
this.hasCooldown.set(true);
|
1409
|
+
this.isEditingMode.set(false);
|
1410
|
+
this.activeEditComment.set(undefined);
|
1411
|
+
}
|
1412
|
+
else {
|
1413
|
+
const payload = {
|
1414
|
+
...this.payload(),
|
1415
|
+
content: this.commentContent(),
|
1416
|
+
contentType: 'text',
|
1417
|
+
isPrivate: isPrivate,
|
1418
|
+
replyId: this.activeReplyComment()?.id ?? null,
|
1419
|
+
};
|
1420
|
+
await this.commentService.insertOne(payload);
|
1421
|
+
this.hasCooldown.set(true);
|
1422
|
+
this.isReplyingMode.set(false);
|
1423
|
+
this.activeReplyComment.set(undefined);
|
1536
1424
|
}
|
1537
|
-
//
|
1425
|
+
// Reload comments to get the updated list with proper hierarchy
|
1426
|
+
const response = await this.commentService.query(this.getPayload());
|
1427
|
+
this.comments.set(response.items);
|
1428
|
+
this.commentContent.set('');
|
1429
|
+
document.getElementsByClassName('ql-editor')[0].innerHTML = '';
|
1430
|
+
setTimeout(() => {
|
1431
|
+
this.hasCooldown.set(false);
|
1432
|
+
}, 1000);
|
1538
1433
|
}
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
top: conversationView.scrollHeight,
|
1549
|
-
behavior: 'smooth',
|
1434
|
+
catch (error) {
|
1435
|
+
console.error('Error submitting comment:', error);
|
1436
|
+
this.toastService.show({
|
1437
|
+
content: typeof error === 'string' ? error : 'Failed to submit comment. Please try again.',
|
1438
|
+
color: 'danger',
|
1439
|
+
location: 'bottom-center',
|
1440
|
+
closeButton: true,
|
1441
|
+
timeOut: 3000,
|
1442
|
+
timeOutProgress: true,
|
1550
1443
|
});
|
1551
1444
|
}
|
1552
|
-
|
1553
|
-
|
1554
|
-
async loadMessages(roomId) {
|
1555
|
-
try {
|
1556
|
-
// Get messages from the chat service
|
1557
|
-
const messageResponse = await this.chatService.getChatMessages(roomId);
|
1558
|
-
// Convert to chat preview format
|
1559
|
-
const chatPreviewMessages = convertToChatPreview(messageResponse);
|
1560
|
-
// Set the messages in the conversation service
|
1561
|
-
// this.conversationService.chats.set(chatPreviewMessages);
|
1445
|
+
finally {
|
1446
|
+
this.isSubmitting.set(false);
|
1562
1447
|
}
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1448
|
+
}
|
1449
|
+
scrollMain() {
|
1450
|
+
const comment = this.isReplyingMode() ? this.activeReplyComment() : this.activeEditComment();
|
1451
|
+
if (comment && comment.id) {
|
1452
|
+
const el = document.getElementById(comment.id);
|
1453
|
+
if (el) {
|
1454
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
1455
|
+
const content = el?.firstElementChild?.children[1];
|
1456
|
+
if (content) {
|
1457
|
+
const prevBg = content.style.background;
|
1458
|
+
content.style.borderRadius = '0.25rem';
|
1459
|
+
content.style.transition = 'background 1s ease-in-out';
|
1460
|
+
content.style.background = `rgba(var(--ax-color-on-surface), var(--tw-bg-opacity))`;
|
1461
|
+
setTimeout(() => {
|
1462
|
+
content.style.background = prevBg || 'rgba(0, 0, 0, 0)';
|
1463
|
+
}, 1000);
|
1464
|
+
}
|
1465
|
+
}
|
1567
1466
|
}
|
1568
1467
|
}
|
1569
|
-
|
1570
|
-
|
1468
|
+
calcDefrenetTime(date) {
|
1469
|
+
return date ? Date.now() - date.getTime() : undefined;
|
1571
1470
|
}
|
1572
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
1573
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", 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: ["chatBoxHeight", "isReplyArrowShown"], 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", inputs: ["chatData"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
1471
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
1472
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentListViewComponent, isStandalone: true, selector: "axm-comment-list-view", viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }, { propertyName: "wysiwyg", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-mt-2\">\n <ax-comment-container>\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (checkImageExists(comment.id!) && comment.author && extractInitials(comment.author) !== '?') {\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(comment.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(comment.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (reply && checkImageExists(reply.id!) && reply.author && extractInitials(reply.author) !== '?') {\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(reply.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(reply.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteReply(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <ax-form>\n <ax-form-field>\n <div>\n @if (isReplyingMode() || isEditingMode()) {\n <div\n class=\"ax-flex ax-justify-between ax-rounded-b-none ax-border ax-border-b-0 ax-rounded-lg ax-bg-on-surface ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\"\n >\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i\n [class]=\"isReplyingMode() ? 'fa-reply' : 'fa-pen'\"\n class=\"fa-solid ax-text-primary-500 dark:ax-text-primary-300 ax-text-2xl ax-me-4\"\n ></i>\n <div\n class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\"\n >\n <p class=\"ax-text-primary-500 dark:ax-text-primary-300\">\n {{ isReplyingMode() ? 'Reply to ' : 'Edit Message' }}\n <span class=\"ax-font-bold\">\n {{ isReplyingMode() ? extractInitials(activeReplyComment()?.author) : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-toolbar>\n <ax-content>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-content>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown()\"\n type=\"submit\"\n color=\"primary\"\n mode=\"split\"\n text=\"Send\"\n (onClick)=\"submitComment()\"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n <ax-button-item-list>\n <ax-button-item (click)=\"submitComment(true)\" text=\"Send Private ...\" name=\"private\" data=\"private\">\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-user-secret\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </ax-suffix>\n </ax-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>\n", styles: ["ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i1.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i1.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i5$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i6.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i7.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i9.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i10.AXFormatPipe, name: "format" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i11.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i11.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i11.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "component", type: i12.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i14.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
1574
1473
|
}
|
1575
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
1474
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentListViewComponent, decorators: [{
|
1576
1475
|
type: Component,
|
1577
|
-
args: [{ selector: 'axm-
|
1578
|
-
|
1476
|
+
args: [{ selector: 'axm-comment-list-view', imports: [
|
1477
|
+
AXWysiwygModule,
|
1478
|
+
AXConversationModule,
|
1479
|
+
AXSkeletonModule,
|
1480
|
+
AXDecoratorModule,
|
1481
|
+
AXCommentModule,
|
1482
|
+
AXButtonModule,
|
1483
|
+
AXAvatarModule,
|
1484
|
+
AXImageModule,
|
1485
|
+
AXLoadingModule,
|
1486
|
+
AXDropdownModule,
|
1487
|
+
AXFormatModule,
|
1488
|
+
AsyncPipe,
|
1489
|
+
AXFormModule,
|
1490
|
+
AXToolBarModule,
|
1491
|
+
FormsModule,
|
1492
|
+
AXDropdownButtonModule,
|
1493
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-mt-2\">\n <ax-comment-container>\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (checkImageExists(comment.id!) && comment.author && extractInitials(comment.author) !== '?') {\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(comment.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(comment.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (reply && checkImageExists(reply.id!) && reply.author && extractInitials(reply.author) !== '?') {\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(reply.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(reply.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteReply(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <ax-form>\n <ax-form-field>\n <div>\n @if (isReplyingMode() || isEditingMode()) {\n <div\n class=\"ax-flex ax-justify-between ax-rounded-b-none ax-border ax-border-b-0 ax-rounded-lg ax-bg-on-surface ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\"\n >\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i\n [class]=\"isReplyingMode() ? 'fa-reply' : 'fa-pen'\"\n class=\"fa-solid ax-text-primary-500 dark:ax-text-primary-300 ax-text-2xl ax-me-4\"\n ></i>\n <div\n class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\"\n >\n <p class=\"ax-text-primary-500 dark:ax-text-primary-300\">\n {{ isReplyingMode() ? 'Reply to ' : 'Edit Message' }}\n <span class=\"ax-font-bold\">\n {{ isReplyingMode() ? extractInitials(activeReplyComment()?.author) : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-toolbar>\n <ax-content>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-content>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown()\"\n type=\"submit\"\n color=\"primary\"\n mode=\"split\"\n text=\"Send\"\n (onClick)=\"submitComment()\"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n <ax-button-item-list>\n <ax-button-item (click)=\"submitComment(true)\" text=\"Send Private ...\" name=\"private\" data=\"private\">\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-user-secret\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </ax-suffix>\n </ax-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>\n", styles: ["ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}\n"] }]
|
1494
|
+
}] });
|
1579
1495
|
|
1580
|
-
|
1581
|
-
|
1582
|
-
*/
|
1583
|
-
class AXMCommentService {
|
1496
|
+
//#region ---- Abstract Chat Service ----
|
1497
|
+
class AXMChatService {
|
1584
1498
|
}
|
1585
|
-
|
1499
|
+
//#endregion
|
1500
|
+
class AXMChatServiceImpl {
|
1586
1501
|
constructor() {
|
1587
1502
|
this.roomService = inject(AXMRoomService);
|
1588
1503
|
this.messageService = inject(AXMMessageService);
|
1589
1504
|
this.sessionService = inject(AXPSessionService);
|
1590
1505
|
this.usersService = inject(AXMUsersEntityService);
|
1506
|
+
this._messageSent$ = new Subject();
|
1507
|
+
this.messageSent$ = this._messageSent$.asObservable();
|
1591
1508
|
}
|
1592
|
-
|
1509
|
+
//#region ---- Helper Methods ----
|
1593
1510
|
getCurrentUser() {
|
1594
1511
|
const user = this.sessionService.user;
|
1595
1512
|
return {
|
@@ -1602,740 +1519,1119 @@ class AXMCommentServiceImpl {
|
|
1602
1519
|
const user = await this.usersService.getOne(userId);
|
1603
1520
|
return {
|
1604
1521
|
id: user.id,
|
1605
|
-
|
1606
|
-
firstName: user.firstName || '',
|
1607
|
-
lastName: user.lastName || '',
|
1608
|
-
fullName: `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'Unknown User',
|
1609
|
-
avatar: '',
|
1522
|
+
type: 'user',
|
1610
1523
|
};
|
1611
1524
|
}
|
1612
1525
|
catch (error) {
|
1613
1526
|
console.error(`Failed to get user info for ID: ${userId}`, error);
|
1614
1527
|
return {
|
1615
1528
|
id: userId,
|
1616
|
-
|
1617
|
-
firstName: 'Unknown',
|
1618
|
-
lastName: 'User',
|
1619
|
-
fullName: 'Unknown User',
|
1620
|
-
avatar: '',
|
1529
|
+
type: 'user',
|
1621
1530
|
};
|
1622
1531
|
}
|
1623
1532
|
}
|
1624
|
-
|
1625
|
-
return
|
1533
|
+
formatMessage(message) {
|
1534
|
+
return message;
|
1626
1535
|
}
|
1627
|
-
async
|
1536
|
+
async getUnreadCount(roomId) {
|
1537
|
+
const { items } = await this.messageService.query({
|
1538
|
+
skip: 0,
|
1539
|
+
take: 99,
|
1540
|
+
filter: { field: 'roomId', value: roomId, operator: { type: 'equal' } },
|
1541
|
+
});
|
1628
1542
|
const currentUserId = this.getCurrentUser().id;
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
return {
|
1636
|
-
...message,
|
1637
|
-
author: {
|
1638
|
-
...message.author,
|
1639
|
-
...userInfo,
|
1640
|
-
},
|
1641
|
-
isFromCurrentUser: message.author.id === currentUserId,
|
1642
|
-
formattedDate: new Date(message.createdAt || new Date()).toLocaleString(),
|
1643
|
-
isLiked,
|
1644
|
-
reactionsCount,
|
1645
|
-
replies: [],
|
1646
|
-
};
|
1543
|
+
const unreadMessages = items.filter((message) => {
|
1544
|
+
const isFromCurrentUser = message.author.id === currentUserId;
|
1545
|
+
const isReadByCurrentUser = (message.seen || []).some((seen) => seen.author.id === currentUserId);
|
1546
|
+
return !isFromCurrentUser && !isReadByCurrentUser;
|
1547
|
+
});
|
1548
|
+
return unreadMessages.length;
|
1647
1549
|
}
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
for (const comment of comments) {
|
1654
|
-
commentMap.set(comment.id, comment);
|
1655
|
-
// Initialize the replies array if not already present
|
1656
|
-
if (!comment.replies) {
|
1657
|
-
comment.replies = [];
|
1658
|
-
}
|
1659
|
-
// If this is a root comment (no replyId), add it to the root comments array
|
1660
|
-
if (!comment.replyId) {
|
1661
|
-
rootComments.push(comment);
|
1662
|
-
}
|
1663
|
-
}
|
1664
|
-
// Second pass: build the hierarchy by adding replies to their parent comments
|
1665
|
-
for (const comment of comments) {
|
1666
|
-
if (comment.replyId) {
|
1667
|
-
const parentComment = commentMap.get(comment.replyId);
|
1668
|
-
if (parentComment && parentComment.replies) {
|
1669
|
-
parentComment.replies.push(comment);
|
1670
|
-
}
|
1671
|
-
else {
|
1672
|
-
// If parent not found (might be deleted), treat as a root comment
|
1673
|
-
rootComments.push(comment);
|
1674
|
-
}
|
1675
|
-
}
|
1550
|
+
//#endregion
|
1551
|
+
//#region ---- ChatRoomService Implementations ----
|
1552
|
+
async createRoom(memberIds, title) {
|
1553
|
+
if (memberIds.length === 0) {
|
1554
|
+
throw new Error('At least one member is required to create a room');
|
1676
1555
|
}
|
1677
|
-
|
1678
|
-
const
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1556
|
+
const currentUser = this.getCurrentUser();
|
1557
|
+
const allMemberIds = [...new Set([...memberIds, currentUser.id])];
|
1558
|
+
const memberReferences = await Promise.all(allMemberIds.map((id) => this.getUserInfo(id)));
|
1559
|
+
const userNames = await Promise.all(memberReferences.map(async (ref) => {
|
1560
|
+
const user = await this.usersService.getOne(ref.id);
|
1561
|
+
return `${user.firstName} ${user.lastName}`.trim();
|
1562
|
+
}));
|
1563
|
+
const room = {
|
1564
|
+
members: memberReferences,
|
1565
|
+
title: title,
|
1566
|
+
topic: memberIds.length > 2 ? 'group' : 'personal',
|
1567
|
+
createdAt: new Date(),
|
1568
|
+
updatedAt: new Date(),
|
1569
|
+
createdBy: this.getCurrentUser().id,
|
1570
|
+
updatedBy: this.getCurrentUser().id,
|
1682
1571
|
};
|
1683
|
-
|
1684
|
-
|
1685
|
-
for (const comment of rootComments) {
|
1686
|
-
if (comment.replies && comment.replies.length > 0) {
|
1687
|
-
comment.replies.sort(sortByDate);
|
1688
|
-
}
|
1689
|
-
}
|
1690
|
-
return rootComments;
|
1572
|
+
const roomId = await this.roomService.insertOne(room);
|
1573
|
+
return this.getRoom(roomId);
|
1691
1574
|
}
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1575
|
+
async getRoom(roomId) {
|
1576
|
+
const room = await this.roomService.getOne(roomId);
|
1577
|
+
if (!room) {
|
1578
|
+
throw new Error(`Room with ID ${roomId} not found.`);
|
1579
|
+
}
|
1580
|
+
const messages = await this.getMessages(roomId, 0, 1);
|
1581
|
+
const lastMessage = messages.items.length > 0 ? messages.items[0] : undefined;
|
1582
|
+
const unreadCount = await this.getUnreadCount(roomId);
|
1583
|
+
const currentUserId = this.getCurrentUser().id;
|
1584
|
+
return {
|
1585
|
+
...room,
|
1586
|
+
lastMessage,
|
1587
|
+
unreadCount,
|
1588
|
+
members: room.members.filter((m) => m.id !== currentUserId),
|
1589
|
+
};
|
1695
1590
|
}
|
1696
|
-
async
|
1697
|
-
|
1591
|
+
async listRooms(skip = 0, take = 100) {
|
1592
|
+
const { items, total } = await this.roomService.query({ skip, take });
|
1593
|
+
const settledRooms = await Promise.allSettled(items.map((room) => this.getRoom(room.id)));
|
1594
|
+
const chatRooms = settledRooms
|
1595
|
+
.filter((result) => {
|
1596
|
+
if (result.status === 'rejected') {
|
1597
|
+
console.error('Failed to fetch room details:', result.reason);
|
1598
|
+
return false;
|
1599
|
+
}
|
1600
|
+
return true;
|
1601
|
+
})
|
1602
|
+
.map((result) => result.value);
|
1603
|
+
return { items: chatRooms, total: chatRooms.length };
|
1698
1604
|
}
|
1699
|
-
async
|
1700
|
-
|
1605
|
+
async updateRoom(roomId, data) {
|
1606
|
+
const update = { ...data, updatedAt: new Date(), updatedBy: this.getCurrentUser().id };
|
1607
|
+
return this.roomService.updateOne(roomId, update);
|
1701
1608
|
}
|
1702
|
-
async
|
1703
|
-
const roomId = this.formatRoomId(entityId, instanceId, roomType);
|
1609
|
+
async deleteRoom(roomId) {
|
1704
1610
|
try {
|
1705
|
-
|
1706
|
-
|
1707
|
-
return roomId;
|
1611
|
+
await this.roomService.deleteOne(roomId);
|
1612
|
+
return true;
|
1708
1613
|
}
|
1709
1614
|
catch (error) {
|
1710
|
-
|
1711
|
-
const roomData = {
|
1712
|
-
id: roomId, // Provide the ID directly for comments
|
1713
|
-
title: `Comments for ${entityId}:${instanceId}`,
|
1714
|
-
members: [this.getCurrentUser()],
|
1715
|
-
};
|
1716
|
-
// Create a new room or return the ID if it already exists
|
1717
|
-
try {
|
1718
|
-
const result = await this.createRoom(roomData);
|
1719
|
-
return typeof result === 'string' ? result : roomId;
|
1720
|
-
}
|
1721
|
-
catch (e) {
|
1722
|
-
// If we get a duplicate error, the room was created by another request
|
1723
|
-
return roomId;
|
1724
|
-
}
|
1615
|
+
return false;
|
1725
1616
|
}
|
1726
1617
|
}
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
skip,
|
1738
|
-
take,
|
1739
|
-
filter: {
|
1740
|
-
field: 'roomId',
|
1741
|
-
value: roomId,
|
1742
|
-
operator: { type: 'equal' },
|
1743
|
-
},
|
1618
|
+
async addParticipant(roomId, userId) {
|
1619
|
+
const room = await this.roomService.getOne(roomId);
|
1620
|
+
if (room.members.some((m) => m.id === userId))
|
1621
|
+
return room;
|
1622
|
+
const userRef = await this.getUserInfo(userId);
|
1623
|
+
const updatedMembers = [...room.members, userRef];
|
1624
|
+
return this.roomService.updateOne(roomId, {
|
1625
|
+
members: updatedMembers,
|
1626
|
+
updatedAt: new Date(),
|
1627
|
+
updatedBy: this.getCurrentUser().id,
|
1744
1628
|
});
|
1745
|
-
// Transform messages to comments
|
1746
|
-
const comments = await Promise.all(result.items.map((message) => this.formatComment(message)));
|
1747
|
-
// Build comment hierarchy
|
1748
|
-
const hierarchicalComments = await this.buildCommentHierarchy(comments);
|
1749
|
-
return {
|
1750
|
-
items: hierarchicalComments,
|
1751
|
-
total: result.total,
|
1752
|
-
};
|
1753
1629
|
}
|
1754
|
-
async
|
1755
|
-
const
|
1756
|
-
|
1630
|
+
async removeParticipant(roomId, userId) {
|
1631
|
+
const room = await this.roomService.getOne(roomId);
|
1632
|
+
const updatedMembers = room.members.filter((member) => member.id !== userId);
|
1633
|
+
return this.roomService.updateOne(roomId, {
|
1634
|
+
members: updatedMembers,
|
1635
|
+
updatedAt: new Date(),
|
1636
|
+
updatedBy: this.getCurrentUser().id,
|
1637
|
+
});
|
1757
1638
|
}
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
const
|
1762
|
-
|
1763
|
-
const messageContent = {
|
1764
|
-
content,
|
1765
|
-
contentType,
|
1766
|
-
};
|
1767
|
-
// Create the message object
|
1639
|
+
//#endregion
|
1640
|
+
//#region ---- ChatMessageService Implementations ----
|
1641
|
+
async sendMessage(roomId, content, contentType = 'text', replyId) {
|
1642
|
+
const author = this.getCurrentUser();
|
1643
|
+
const messageContent = { content, contentType };
|
1768
1644
|
const message = {
|
1769
1645
|
roomId,
|
1770
1646
|
message: messageContent,
|
1771
|
-
author
|
1772
|
-
replyId
|
1647
|
+
author,
|
1648
|
+
replyId,
|
1649
|
+
createdAt: new Date(),
|
1650
|
+
updatedAt: new Date(),
|
1651
|
+
createdBy: this.getCurrentUser().id,
|
1652
|
+
updatedBy: this.getCurrentUser().id,
|
1653
|
+
reactions: [],
|
1654
|
+
seen: [],
|
1773
1655
|
};
|
1774
|
-
|
1775
|
-
const
|
1776
|
-
|
1777
|
-
|
1778
|
-
return
|
1656
|
+
const messageId = await this.messageService.insertOne({ ...message });
|
1657
|
+
const newMessage = await this.messageService.getOne(messageId);
|
1658
|
+
const formattedMessage = this.formatMessage(newMessage);
|
1659
|
+
this._messageSent$.next(formattedMessage);
|
1660
|
+
return formattedMessage;
|
1779
1661
|
}
|
1780
|
-
async
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
}
|
1789
|
-
// Apply the update
|
1790
|
-
const result = await this.messageService.updateOne(id, messageUpdate);
|
1791
|
-
// Return as a comment entity
|
1792
|
-
return this.formatComment(result);
|
1662
|
+
async getMessages(roomId, skip = 0, take = 99) {
|
1663
|
+
const result = await this.messageService.query({
|
1664
|
+
skip,
|
1665
|
+
take,
|
1666
|
+
filter: { field: 'roomId', value: roomId, operator: { type: 'equal' } },
|
1667
|
+
sort: [{ field: 'createdAt', dir: 'desc' }],
|
1668
|
+
});
|
1669
|
+
const formattedMessages = result.items.map((m) => this.formatMessage(m));
|
1670
|
+
return { items: formattedMessages, total: result.total };
|
1793
1671
|
}
|
1794
|
-
async
|
1672
|
+
async getMessage(messageId) {
|
1673
|
+
const message = await this.messageService.getOne(messageId);
|
1674
|
+
return this.formatMessage(message);
|
1675
|
+
}
|
1676
|
+
async editMessage(messageId, content, contentType = 'text') {
|
1677
|
+
const messageContent = { content, contentType };
|
1678
|
+
return this.messageService.updateOne(messageId, {
|
1679
|
+
message: messageContent,
|
1680
|
+
updatedAt: new Date(),
|
1681
|
+
updatedBy: this.getCurrentUser().id,
|
1682
|
+
});
|
1683
|
+
}
|
1684
|
+
async deleteMessage(messageId) {
|
1795
1685
|
try {
|
1796
|
-
await this.messageService.deleteOne(
|
1686
|
+
await this.messageService.deleteOne(messageId);
|
1797
1687
|
return true;
|
1798
1688
|
}
|
1799
1689
|
catch (error) {
|
1800
|
-
console.error('Failed to delete comment:', error);
|
1801
1690
|
return false;
|
1802
1691
|
}
|
1803
1692
|
}
|
1804
|
-
|
1805
|
-
|
1693
|
+
async replyMessage(replyToMessageId, content, contentType = 'text') {
|
1694
|
+
const originalMessage = await this.messageService.getOne(replyToMessageId);
|
1695
|
+
return this.sendMessage(originalMessage.roomId, content, contentType, replyToMessageId);
|
1696
|
+
}
|
1697
|
+
async pinMessage(messageId) {
|
1698
|
+
return this.messageService.updateOne(messageId, {
|
1699
|
+
isPinned: true,
|
1700
|
+
updatedAt: new Date(),
|
1701
|
+
updatedBy: this.getCurrentUser().id,
|
1702
|
+
});
|
1703
|
+
}
|
1704
|
+
async unpinMessage(messageId) {
|
1705
|
+
return this.messageService.updateOne(messageId, {
|
1706
|
+
isPinned: false,
|
1707
|
+
updatedAt: new Date(),
|
1708
|
+
updatedBy: this.getCurrentUser().id,
|
1709
|
+
});
|
1710
|
+
}
|
1711
|
+
//#endregion
|
1712
|
+
//#region ---- ChatReactionService Implementations ----
|
1713
|
+
async addReaction(messageId, type) {
|
1806
1714
|
const author = this.getCurrentUser();
|
1807
1715
|
const message = await this.messageService.getOne(messageId);
|
1808
|
-
// Check if the user has already liked this message
|
1809
1716
|
const reactions = message.reactions || [];
|
1810
|
-
|
1811
|
-
|
1812
|
-
if (existingReaction) {
|
1813
|
-
// If the reaction exists, remove it (unlike)
|
1814
|
-
updatedReactions = reactions.filter((r) => !(r.author.id === author.id && r.type === 'like'));
|
1717
|
+
if (reactions.some((r) => r.author.id === author.id && r.type === type)) {
|
1718
|
+
return message;
|
1815
1719
|
}
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1720
|
+
const updatedReactions = [...reactions, { author, type }];
|
1721
|
+
return this.messageService.updateOne(messageId, {
|
1722
|
+
reactions: updatedReactions,
|
1723
|
+
updatedAt: new Date(),
|
1724
|
+
updatedBy: this.getCurrentUser().id,
|
1725
|
+
});
|
1726
|
+
}
|
1727
|
+
async removeReaction(messageId, type) {
|
1728
|
+
const author = this.getCurrentUser();
|
1729
|
+
const message = await this.messageService.getOne(messageId);
|
1730
|
+
const updatedReactions = (message.reactions || []).filter((r) => !(r.author.id === author.id && r.type === type));
|
1731
|
+
return this.messageService.updateOne(messageId, {
|
1732
|
+
reactions: updatedReactions,
|
1733
|
+
updatedAt: new Date(),
|
1734
|
+
updatedBy: this.getCurrentUser().id,
|
1735
|
+
});
|
1736
|
+
}
|
1737
|
+
async getReactions(messageId) {
|
1738
|
+
const message = await this.messageService.getOne(messageId);
|
1739
|
+
return message.reactions || [];
|
1740
|
+
}
|
1741
|
+
//#endregion
|
1742
|
+
//#region ---- ChatSeenService Implementations ----
|
1743
|
+
async markSeen(messageId) {
|
1744
|
+
const author = this.getCurrentUser();
|
1745
|
+
const message = await this.messageService.getOne(messageId);
|
1746
|
+
const seen = message.seen || [];
|
1747
|
+
if (seen.some((s) => s.author.id === author.id)) {
|
1748
|
+
return message;
|
1819
1749
|
}
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1750
|
+
const updatedSeen = [...seen, { author, type: 'read' }];
|
1751
|
+
return this.messageService.updateOne(messageId, {
|
1752
|
+
seen: updatedSeen,
|
1753
|
+
updatedAt: new Date(),
|
1754
|
+
updatedBy: this.getCurrentUser().id,
|
1755
|
+
});
|
1824
1756
|
}
|
1825
|
-
async
|
1826
|
-
const
|
1757
|
+
async getSeenBy(messageId) {
|
1758
|
+
const message = await this.messageService.getOne(messageId);
|
1759
|
+
return message.seen || [];
|
1760
|
+
}
|
1761
|
+
async markRoomSeen(roomId) {
|
1827
1762
|
try {
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
}
|
1837
|
-
return
|
1763
|
+
if (!roomId) {
|
1764
|
+
return true;
|
1765
|
+
}
|
1766
|
+
const { items } = await this.getMessages(roomId, 0, 99);
|
1767
|
+
const currentUserId = this.getCurrentUser().id;
|
1768
|
+
const unreadMessages = items.filter((message) => message.author.id !== currentUserId && !message.seen?.some((s) => s.author.id === currentUserId));
|
1769
|
+
if (unreadMessages.length > 0) {
|
1770
|
+
await Promise.all(unreadMessages.map((message) => this.markSeen(message.id)));
|
1771
|
+
}
|
1772
|
+
return true;
|
1838
1773
|
}
|
1839
1774
|
catch (error) {
|
1840
|
-
console.error(
|
1841
|
-
return
|
1775
|
+
console.error(`Error marking room ${roomId} as seen:`, error);
|
1776
|
+
return false;
|
1842
1777
|
}
|
1843
1778
|
}
|
1844
|
-
|
1845
|
-
|
1846
|
-
params: { entityId, instanceId, roomType },
|
1847
|
-
skip,
|
1848
|
-
take,
|
1849
|
-
};
|
1850
|
-
return this.query(queryRequest);
|
1851
|
-
}
|
1852
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
1853
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentServiceImpl, providedIn: 'root' }); }
|
1779
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
1780
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, providedIn: 'root' }); }
|
1854
1781
|
}
|
1855
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
1782
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatServiceImpl, decorators: [{
|
1856
1783
|
type: Injectable,
|
1857
1784
|
args: [{
|
1858
1785
|
providedIn: 'root',
|
1859
1786
|
}]
|
1860
1787
|
}] });
|
1861
1788
|
|
1862
|
-
class
|
1789
|
+
class AXMChatItemComponent {
|
1863
1790
|
constructor() {
|
1864
|
-
|
1865
|
-
this.
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
};
|
1873
|
-
this
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1791
|
+
// Input
|
1792
|
+
this.data = input.required();
|
1793
|
+
// Output
|
1794
|
+
this.pressChatItem = output();
|
1795
|
+
// Services
|
1796
|
+
this.sessionService = inject(AXPSessionService);
|
1797
|
+
this.userService = inject(AXMUsersEntityService);
|
1798
|
+
this.datePipe = inject(DatePipe);
|
1799
|
+
this.chatName = signal({ fullName: 'Loading...', id: '' });
|
1800
|
+
this.#chatInfoEffect = effect(async () => {
|
1801
|
+
const { members, title, id } = this.data();
|
1802
|
+
if (members.length > 1) {
|
1803
|
+
this.chatName.set({
|
1804
|
+
fullName: title || 'Group Chat',
|
1805
|
+
id,
|
1806
|
+
});
|
1807
|
+
return;
|
1808
|
+
}
|
1809
|
+
const member = members[0];
|
1810
|
+
if (!member) {
|
1811
|
+
this.chatName.set({ fullName: 'Unknown User', id: '' });
|
1812
|
+
return;
|
1813
|
+
}
|
1814
|
+
const data = await this.userService.getOne(member.id);
|
1815
|
+
this.chatName.set({
|
1816
|
+
fullName: `${data.firstName} ${data.lastName}`,
|
1817
|
+
id: member.id,
|
1818
|
+
});
|
1819
|
+
});
|
1820
|
+
this.formattedLastMessageDate = computed(() => {
|
1821
|
+
const lastMessage = this.data().lastMessage;
|
1822
|
+
if (!lastMessage?.createdAt) {
|
1823
|
+
return '';
|
1824
|
+
}
|
1825
|
+
const messageDate = new Date(lastMessage.createdAt);
|
1826
|
+
const today = new Date();
|
1827
|
+
const isToday = messageDate.getFullYear() === today.getFullYear() &&
|
1828
|
+
messageDate.getMonth() === today.getMonth() &&
|
1829
|
+
messageDate.getDate() === today.getDate();
|
1830
|
+
if (isToday) {
|
1831
|
+
return this.datePipe.transform(messageDate, 'shortTime');
|
1832
|
+
}
|
1833
|
+
else {
|
1834
|
+
return this.datePipe.transform(messageDate, 'mediumDate');
|
1835
|
+
}
|
1836
|
+
});
|
1837
|
+
this.lastMessage = computed(() => {
|
1838
|
+
const message = this.data().lastMessage?.message;
|
1839
|
+
if (!message)
|
1840
|
+
return { content: 'No messages yet', contentType: 'text' };
|
1841
|
+
return {
|
1842
|
+
content: message.contentType === 'text' ? message.content : message.contentType,
|
1843
|
+
contentType: message.contentType,
|
1844
|
+
};
|
1845
|
+
});
|
1846
|
+
this.hasUnread = computed(() => {
|
1847
|
+
return this.data().unreadCount > 0;
|
1848
|
+
});
|
1849
|
+
this.unreadCount = computed(() => {
|
1850
|
+
return this.data().unreadCount.toString();
|
1879
1851
|
});
|
1880
1852
|
}
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1853
|
+
#chatInfoEffect;
|
1854
|
+
onPressChatItem(id) {
|
1855
|
+
this.pressChatItem.emit(id);
|
1856
|
+
}
|
1857
|
+
messageSeenStatus() {
|
1858
|
+
const lastMessage = this.data().lastMessage;
|
1859
|
+
if (!lastMessage)
|
1860
|
+
return '';
|
1861
|
+
// Check if current user is the author
|
1862
|
+
if (this.sessionService.user?.id === lastMessage.author?.id) {
|
1863
|
+
// Check if message has been seen by anyone
|
1864
|
+
return lastMessage.seen.length > 0 ? 'ax-icon-dobble-check' : 'ax-icon-check';
|
1865
|
+
}
|
1866
|
+
return '';
|
1867
|
+
}
|
1868
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
1869
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMChatItemComponent, isStandalone: true, selector: "axm-chat-item", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { pressChatItem: "pressChatItem" }, ngImport: i0, template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-2\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-pt-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n <!-- @default {\n <i class=\"fa-regular fa-message\"></i>\n } -->\n }\n <p class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </p>\n </div>\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
1897
1870
|
}
|
1898
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
1871
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatItemComponent, decorators: [{
|
1899
1872
|
type: Component,
|
1900
|
-
args: [{
|
1901
|
-
template: `<axp-widgets-container
|
1902
|
-
class="ax-flex ax-flex-col ax-gap-2 ax-p-4"
|
1903
|
-
[context]="context"
|
1904
|
-
(onContextChanged)="context = $event.data"
|
1905
|
-
>
|
1906
|
-
<div class="ax-m-5">
|
1907
|
-
<ng-container axp-widget-renderer [node]="lookupNode" [mode]="'edit'"> </ng-container>
|
1908
|
-
</div>
|
1909
|
-
</axp-widgets-container>
|
1910
|
-
|
1911
|
-
<ax-footer>
|
1912
|
-
<ax-suffix>
|
1913
|
-
<ax-button text="Accept & Send" color="primary" (onClick)="handleClose()"></ax-button>
|
1914
|
-
</ax-suffix>
|
1915
|
-
</ax-footer>`,
|
1916
|
-
imports: [AXDecoratorModule, AXPLayoutBuilderModule, AXButtonModule],
|
1917
|
-
}]
|
1873
|
+
args: [{ selector: 'axm-chat-item', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXDecoratorModule, AXBadgeModule, AXPUserAvatarComponent], template: "<div\n [id]=\"data().id\"\n class=\"ax-cursor-pointer ax-p-4 ax-flex ax-items-center ax-justify-between ax-gap-4 ax-border-b ax-border-divider\"\n (click)=\"onPressChatItem(data().id)\"\n>\n <!-- Avatar and User Info Section -->\n <div class=\"ax-flex ax-min-w-0 ax-gap-4 ax-flex-1\">\n <!-- Avatar with Online Status -->\n <div class=\"ax-flex-shrink-0\">\n <axp-user-avatar [size]=\"48\" [userId]=\"chatName().id\"></axp-user-avatar>\n <!--Temp-->\n </div>\n\n <!-- User Details and Last Message -->\n <div class=\"ax-flex ax-flex-col ax-min-w-0 ax-flex-1 ax-gap-2\">\n <div class=\"ax-flex ax-items-center ax-justify-between ax-gap-2\">\n <p class=\"ax-font-semibold ax-text-on-surface ax-truncate ax-pt-1\" [title]=\"chatName().fullName\">\n {{ chatName().fullName }}\n </p>\n <div class=\"ax-flex ax-items-center ax-gap-1\">\n @if (messageSeenStatus()) {\n <ax-icon class=\"ax-icon ax-text-success ax-size-5\" [class]=\"messageSeenStatus()\"></ax-icon>\n }\n @if (this.data().lastMessage) {\n <span class=\"ax-text-xs ax-whitespace-nowrap\">\n {{ formattedLastMessageDate() }}\n </span>\n }\n </div>\n </div>\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-mt-1 ax-overflow-hidden\">\n <div class=\"ax-flex ax-items-center ax-gap-2 ax-text-sm ax-shrink ax-min-w-0\">\n @switch (lastMessage().contentType) {\n @case ('image') {\n <i class=\"fa-regular fa-image\"></i>\n }\n @case ('video') {\n <i class=\"fa-regular fa-video\"></i>\n }\n @case ('file') {\n <i class=\"fa-regular fa-file\"></i>\n }\n @case ('audio') {\n <i class=\"fa-regular fa-microphone\"></i>\n }\n @case ('link') {\n <i class=\"fa-regular fa-link\"></i>\n }\n <!-- @default {\n <i class=\"fa-regular fa-message\"></i>\n } -->\n }\n <p class=\"ax-truncate ax-capitalize\">\n {{ lastMessage().content }}\n </p>\n </div>\n @if (hasUnread()) {\n <ax-badge color=\"primary\" [text]=\"unreadCount()\" class=\"ax-flex-shrink-0 ax-ml-auto\"></ax-badge>\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}\n"] }]
|
1918
1874
|
}] });
|
1919
1875
|
|
1920
|
-
class
|
1876
|
+
class AXMChatComponent extends AXPPageLayoutBaseComponent {
|
1921
1877
|
constructor() {
|
1922
|
-
|
1923
|
-
this.
|
1924
|
-
this.
|
1925
|
-
this.
|
1926
|
-
this.isEditingMode = signal(false);
|
1927
|
-
this.isLoading = signal(true);
|
1928
|
-
this.failedImageIds = signal([]);
|
1929
|
-
this.activeReplyComment = signal(undefined);
|
1930
|
-
this.activeEditComment = signal(undefined);
|
1931
|
-
this.wysiwygEditor = viewChild.required('w');
|
1932
|
-
this.commentService = inject(AXMCommentService);
|
1933
|
-
this.platform = inject(AXPlatform);
|
1934
|
-
this.route = inject(ActivatedRoute);
|
1878
|
+
super(...arguments);
|
1879
|
+
this.activatedRoute = inject(ActivatedRoute);
|
1880
|
+
this.chatService = inject(AXMChatService);
|
1881
|
+
this.userService = inject(AXMUsersEntityService);
|
1935
1882
|
this.popupService = inject(AXPopupService);
|
1936
|
-
this.
|
1937
|
-
this.
|
1938
|
-
|
1939
|
-
this.
|
1940
|
-
this.
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
1945
|
-
this.
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
this.
|
1951
|
-
this.
|
1952
|
-
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
1883
|
+
this.router = inject(Router);
|
1884
|
+
this.unsubscribe = inject(AXUnsubscriber);
|
1885
|
+
// View Children
|
1886
|
+
this.tab = viewChild('tab');
|
1887
|
+
this.container = viewChild('container');
|
1888
|
+
// State signals
|
1889
|
+
this.rooms = signal([]);
|
1890
|
+
this.allRooms = signal([]);
|
1891
|
+
this.selectedRoom = signal(null);
|
1892
|
+
this.isLoading = signal(false);
|
1893
|
+
this.error = signal(null);
|
1894
|
+
this.activeTab = signal(0);
|
1895
|
+
this.isSearching = signal(false);
|
1896
|
+
this.searchQuery = signal('');
|
1897
|
+
this.showSearch = signal(true);
|
1898
|
+
this.placeholder = signal('Search chats...');
|
1899
|
+
// Computed signals
|
1900
|
+
// filteredRooms = computed(() => {
|
1901
|
+
// let chatRooms = this.rooms();
|
1902
|
+
// // Apply search filter
|
1903
|
+
// if (this.searchQuery()) {
|
1904
|
+
// const searchText = this.searchQuery().toLowerCase();
|
1905
|
+
// chatRooms = chatRooms.filter(async (room) => {
|
1906
|
+
// const user = await this.userService.getOne(room.members?.[0]?.id);
|
1907
|
+
// const roomName = `${user.firstName} ${user.lastName}` || '';
|
1908
|
+
// const lastMessage = room.lastMessage?.message?.content || '';
|
1909
|
+
// return roomName.toLowerCase().includes(searchText) || lastMessage.toLowerCase().includes(searchText);
|
1910
|
+
// });
|
1911
|
+
// }
|
1912
|
+
// // Apply tab filter
|
1913
|
+
// if (this.activeTab() === 1) {
|
1914
|
+
// return chatRooms.filter((room) => room.unreadCount > 0);
|
1915
|
+
// }
|
1916
|
+
// return chatRooms;
|
1917
|
+
// });
|
1918
|
+
this.filteredRooms = signal([]);
|
1919
|
+
this.searchEffect = effect(async () => {
|
1920
|
+
let chatRooms = this.allRooms();
|
1921
|
+
// Search filter
|
1922
|
+
const query = (this.searchQuery() || '').toLowerCase();
|
1923
|
+
if (query) {
|
1924
|
+
// Perform async enrichment for names
|
1925
|
+
const filtered = [];
|
1926
|
+
for (const room of chatRooms) {
|
1927
|
+
try {
|
1928
|
+
let roomName = '';
|
1929
|
+
// Handle group chats vs. single-user chats
|
1930
|
+
if (room.members && room.members.length > 1) {
|
1931
|
+
roomName = room.title || '';
|
1932
|
+
}
|
1933
|
+
else if (room.members && room.members.length === 1) {
|
1934
|
+
const memberId = room.members[0].id;
|
1935
|
+
if (memberId) {
|
1936
|
+
const user = await this.userService.getOne(memberId);
|
1937
|
+
roomName = `${user.firstName} ${user.lastName}` || '';
|
1938
|
+
}
|
1939
|
+
}
|
1940
|
+
const lastMessage = room.lastMessage?.message?.content || '';
|
1941
|
+
if (roomName.toLowerCase().includes(query) || lastMessage.toLowerCase().includes(query)) {
|
1942
|
+
filtered.push(room);
|
1943
|
+
}
|
1944
|
+
}
|
1945
|
+
catch (error) {
|
1946
|
+
console.error(`Failed to process search for room ${room.id}`, error);
|
1947
|
+
}
|
1948
|
+
}
|
1949
|
+
chatRooms = filtered;
|
1950
|
+
}
|
1951
|
+
// Tab filter
|
1952
|
+
if (this.activeTab() === 1) {
|
1953
|
+
chatRooms = chatRooms.filter((room) => room.unreadCount > 0);
|
1954
|
+
}
|
1955
|
+
this.filteredRooms.set(chatRooms);
|
1959
1956
|
});
|
1960
|
-
this.
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1957
|
+
this.unreadCount = computed(() => this.allRooms().filter((i) => i.unreadCount > 0).length);
|
1958
|
+
this.allCount = computed(() => this.allRooms().length);
|
1959
|
+
this.hasUnread = computed(() => this.allRooms().filter((i) => i.unreadCount > 0).length > 0);
|
1960
|
+
this.totalCount = computed(() => this.allRooms().length || 0);
|
1961
|
+
this.af = afterNextRender(() => {
|
1962
|
+
const tabComponent = this.tab();
|
1963
|
+
if (tabComponent) {
|
1964
|
+
tabComponent.onActiveTabChanged.subscribe((i) => {
|
1965
|
+
this.activeTab.set(i.index);
|
1966
|
+
});
|
1964
1967
|
}
|
1965
|
-
|
1966
|
-
|
1967
|
-
result: isValid,
|
1968
|
-
message: isValid ? '' : 'Please fill the content',
|
1969
|
-
value: content,
|
1970
|
-
};
|
1971
|
-
};
|
1968
|
+
//(window as any).a = this.container()?.startSideDrawer();
|
1969
|
+
});
|
1972
1970
|
}
|
1973
|
-
|
1974
|
-
this.
|
1971
|
+
onTabChange(event) {
|
1972
|
+
this.activeTab.set(event.index);
|
1975
1973
|
}
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1974
|
+
async ngOnInit() {
|
1975
|
+
super.ngOnInit();
|
1976
|
+
this.isLoading.set(true);
|
1977
|
+
this.error.set(null);
|
1978
|
+
await this.loadChats();
|
1979
|
+
this.isLoading.set(false);
|
1980
|
+
//
|
1981
|
+
this.router.events
|
1982
|
+
.pipe(this.unsubscribe.takeUntilDestroy, filter((event) => event instanceof NavigationEnd), startWith(null))
|
1983
|
+
.subscribe(async () => {
|
1984
|
+
await this.loadFromRoute();
|
1985
|
+
});
|
1986
|
+
//
|
1987
|
+
this.messageSentSubscription = this.chatService.messageSent$.subscribe(() => {
|
1988
|
+
this.refreshChat();
|
1989
|
+
});
|
1980
1990
|
}
|
1981
|
-
|
1982
|
-
this.
|
1991
|
+
async loadFromRoute() {
|
1992
|
+
const chatId = this.activatedRoute.snapshot.firstChild?.paramMap.get('id');
|
1993
|
+
if (chatId) {
|
1994
|
+
this.markChatAsRead(chatId);
|
1995
|
+
}
|
1996
|
+
else {
|
1997
|
+
this.selectedRoom.set(null);
|
1998
|
+
}
|
1983
1999
|
}
|
1984
|
-
|
1985
|
-
|
2000
|
+
// Page Interface Implementation
|
2001
|
+
async getPageTitle() {
|
2002
|
+
const room = this.selectedRoom();
|
2003
|
+
if (!room) {
|
2004
|
+
return '';
|
2005
|
+
}
|
2006
|
+
const { members, title, id } = room;
|
2007
|
+
if (members.length > 1) {
|
2008
|
+
return title || 'Group Chat';
|
2009
|
+
}
|
2010
|
+
const member = members[0];
|
2011
|
+
if (!member) {
|
2012
|
+
return 'Unknown User';
|
2013
|
+
}
|
2014
|
+
const user = await this.userService.getOne(member.id);
|
2015
|
+
return `${user.firstName} ${user.lastName}` || 'Unknown User';
|
1986
2016
|
}
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
// Handle the case where name is an object with fullName property
|
1991
|
-
const nameStr = typeof name === 'object' && name.fullName ? name.fullName : String(name);
|
1992
|
-
const words = nameStr.split(' ');
|
1993
|
-
const initials = words.map((word) => word.charAt(0).toUpperCase());
|
1994
|
-
return initials.join('');
|
2017
|
+
getPageDescription() {
|
2018
|
+
const room = this.selectedRoom();
|
2019
|
+
return room?.topic || '';
|
1995
2020
|
}
|
1996
|
-
async
|
1997
|
-
this.
|
2021
|
+
async getBackButton() {
|
2022
|
+
if (this.selectedRoom()) {
|
2023
|
+
return {
|
2024
|
+
title: await this.translateService.translateAsync('back-to-chat-list', { scope: 'conversation' }),
|
2025
|
+
};
|
2026
|
+
}
|
2027
|
+
else
|
2028
|
+
return null;
|
2029
|
+
}
|
2030
|
+
onBackButtonClick() {
|
2031
|
+
this.selectedRoom.set(null);
|
2032
|
+
this.router.navigate(['./'], { relativeTo: this.activatedRoute });
|
2033
|
+
}
|
2034
|
+
// Methods
|
2035
|
+
async loadChats() {
|
1998
2036
|
try {
|
1999
|
-
|
2000
|
-
this.
|
2037
|
+
// Use the updated service method to get rooms with last messages
|
2038
|
+
const response = await this.chatService.listRooms();
|
2039
|
+
// Sort rooms by last message createdAt timestamp in descending order
|
2040
|
+
const sortedRooms = response.items.sort((a, b) => {
|
2041
|
+
const dateA = a.lastMessage?.createdAt || a.createdAt || 0;
|
2042
|
+
const dateB = b.lastMessage?.createdAt || b.createdAt || 0;
|
2043
|
+
return new Date(dateB).getTime() - new Date(dateA).getTime();
|
2044
|
+
});
|
2045
|
+
this.allRooms.set(sortedRooms);
|
2046
|
+
this.rooms.set(sortedRooms);
|
2001
2047
|
}
|
2002
2048
|
catch (error) {
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2049
|
+
this.error.set('Failed to load chats. Please try again.');
|
2050
|
+
console.error('Error loading chats:', error);
|
2051
|
+
}
|
2052
|
+
}
|
2053
|
+
async refreshChat() {
|
2054
|
+
await this.loadChats();
|
2055
|
+
}
|
2056
|
+
async markChatAsRead(chatId) {
|
2057
|
+
try {
|
2058
|
+
// Use the service method to mark chat as read
|
2059
|
+
await this.chatService.markRoomSeen(chatId);
|
2060
|
+
// Update local state without making another network request
|
2061
|
+
const currentRooms = this.allRooms();
|
2062
|
+
const updatedRooms = currentRooms.map((room) => {
|
2063
|
+
if (room.id === chatId) {
|
2064
|
+
// Reset unread count for the selected room
|
2065
|
+
return { ...room, unreadCount: 0 };
|
2066
|
+
}
|
2067
|
+
return room;
|
2011
2068
|
});
|
2012
|
-
|
2069
|
+
// Update the rooms signals
|
2070
|
+
this.allRooms.set(updatedRooms);
|
2071
|
+
this.rooms.set(updatedRooms);
|
2072
|
+
// Find the selected room
|
2073
|
+
const selectedRoom = updatedRooms.find((room) => room.id === chatId) || null;
|
2074
|
+
this.selectedRoom.set(selectedRoom);
|
2075
|
+
// Navigate to the chat details
|
2076
|
+
this.router.navigate([chatId], { relativeTo: this.activatedRoute });
|
2013
2077
|
}
|
2014
|
-
|
2015
|
-
|
2016
|
-
this.isLoading.set(false);
|
2017
|
-
}, 250);
|
2078
|
+
catch (error) {
|
2079
|
+
console.error('Failed to mark chat as read:', error);
|
2018
2080
|
}
|
2019
2081
|
}
|
2020
|
-
|
2021
|
-
this.
|
2022
|
-
this.
|
2023
|
-
this.isEditingMode.set(true);
|
2024
|
-
this.activeEditComment.set(comment);
|
2025
|
-
const contentToEdit = reply ? reply.message?.content : comment.message?.content;
|
2026
|
-
this.commentContent.set(contentToEdit || '');
|
2027
|
-
this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
|
2082
|
+
searchChat(query) {
|
2083
|
+
this.searchQuery.set(query);
|
2084
|
+
this.isSearching.set(!!query);
|
2028
2085
|
}
|
2029
|
-
|
2030
|
-
|
2031
|
-
this.
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2086
|
+
// Header methods
|
2087
|
+
onSearch(query) {
|
2088
|
+
this.searchChat(query);
|
2089
|
+
}
|
2090
|
+
clearSearch() {
|
2091
|
+
this.searchQuery.set('');
|
2092
|
+
this.isSearching.set(false);
|
2093
|
+
}
|
2094
|
+
// Footer methods
|
2095
|
+
async onNewConversation() {
|
2096
|
+
const popupConfig = {
|
2097
|
+
header: true,
|
2098
|
+
size: 'sm',
|
2099
|
+
draggable: true,
|
2100
|
+
hasBackdrop: true,
|
2101
|
+
closeButton: true,
|
2102
|
+
closeOnBackdropClick: false,
|
2103
|
+
};
|
2104
|
+
const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
|
2105
|
+
const members = popup.data?.lookup;
|
2106
|
+
try {
|
2107
|
+
if (members) {
|
2108
|
+
const res = await this.chatService.createRoom(members, popup.data?.title);
|
2109
|
+
await this.refreshChat();
|
2110
|
+
this.router.navigate([res.id], { relativeTo: this.activatedRoute });
|
2111
|
+
}
|
2112
|
+
}
|
2113
|
+
catch (error) {
|
2114
|
+
this.toastService.danger('Failed to create room');
|
2040
2115
|
}
|
2041
|
-
this.wysiwygEditor().getHostElement().scrollIntoView({ behavior: 'smooth', block: 'start' });
|
2042
2116
|
}
|
2043
|
-
|
2044
|
-
|
2045
|
-
icon: 'fa-regular fa-warning',
|
2046
|
-
content: 'Are you sure you want to delete this comment?',
|
2047
|
-
title: 'Delete Comment',
|
2048
|
-
type: 'danger',
|
2049
|
-
orientation: 'horizontal',
|
2050
|
-
buttons: [
|
2051
|
-
{
|
2052
|
-
text: 'Delete',
|
2053
|
-
color: 'danger',
|
2054
|
-
onClick: async (e) => {
|
2055
|
-
e.handled = true;
|
2056
|
-
e.source.text = 'Deleting...';
|
2057
|
-
e.source.disabled = true;
|
2058
|
-
e.source.loading = true;
|
2059
|
-
try {
|
2060
|
-
if (comment.id) {
|
2061
|
-
await this.commentService.deleteOne(comment.id);
|
2062
|
-
this.removeMessageById(comment.id);
|
2063
|
-
this.toastService.show({
|
2064
|
-
content: 'Comment deleted successfully.',
|
2065
|
-
color: 'success',
|
2066
|
-
location: 'bottom-center',
|
2067
|
-
closeButton: true,
|
2068
|
-
timeOut: 3000,
|
2069
|
-
timeOutProgress: true,
|
2070
|
-
});
|
2071
|
-
if (this.isEditingMode() && this.activeEditComment()?.id === comment.id) {
|
2072
|
-
this.resetReplyEditState();
|
2073
|
-
}
|
2074
|
-
dialog.close();
|
2075
|
-
}
|
2076
|
-
}
|
2077
|
-
catch (error) {
|
2078
|
-
this.toastService.show({
|
2079
|
-
content: typeof error === 'string' ? error : 'Failed to delete comment!',
|
2080
|
-
color: 'danger',
|
2081
|
-
location: 'bottom-center',
|
2082
|
-
closeButton: true,
|
2083
|
-
timeOut: 3000,
|
2084
|
-
timeOutProgress: true,
|
2085
|
-
});
|
2086
|
-
}
|
2087
|
-
},
|
2088
|
-
},
|
2089
|
-
{
|
2090
|
-
text: 'Cancel',
|
2091
|
-
color: 'default',
|
2092
|
-
autofocus: true,
|
2093
|
-
onClick: (e) => {
|
2094
|
-
dialog.close();
|
2095
|
-
},
|
2096
|
-
},
|
2097
|
-
],
|
2098
|
-
closeButton: false,
|
2099
|
-
});
|
2117
|
+
ngOnDestroy() {
|
2118
|
+
this.messageSentSubscription?.unsubscribe();
|
2100
2119
|
}
|
2101
|
-
|
2102
|
-
|
2103
|
-
|
2104
|
-
|
2105
|
-
|
2106
|
-
|
2107
|
-
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
|
2114
|
-
|
2115
|
-
|
2116
|
-
|
2117
|
-
|
2118
|
-
|
2119
|
-
|
2120
|
-
|
2121
|
-
|
2122
|
-
|
2123
|
-
|
2124
|
-
|
2125
|
-
|
2126
|
-
|
2127
|
-
|
2128
|
-
|
2129
|
-
|
2130
|
-
|
2131
|
-
|
2132
|
-
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2136
|
-
|
2137
|
-
|
2138
|
-
|
2139
|
-
|
2140
|
-
|
2141
|
-
|
2142
|
-
|
2143
|
-
|
2144
|
-
|
2145
|
-
|
2146
|
-
|
2147
|
-
|
2148
|
-
|
2149
|
-
|
2150
|
-
|
2151
|
-
|
2152
|
-
|
2153
|
-
|
2154
|
-
|
2155
|
-
|
2156
|
-
|
2157
|
-
|
2158
|
-
|
2159
|
-
|
2160
|
-
|
2161
|
-
|
2162
|
-
|
2120
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
2121
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", providers: [
|
2122
|
+
{
|
2123
|
+
provide: AXPPageLayoutBase,
|
2124
|
+
useExisting: AXMChatComponent,
|
2125
|
+
},
|
2126
|
+
AXUnsubscriber,
|
2127
|
+
], viewQueries: [{ propertyName: "tab", first: true, predicate: ["tab"], descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout #container>\n <axp-layout-start-side [axResizable]=\"true\" class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>{{ 'module-name' | translate: { scope: 'conversation' } | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0\">\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content style=\"height: calc(100vh - 22rem)\">\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<ng-template #template>\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n @if (hasUnread()) {\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"onTabChange($event)\"\n >\n <ax-tab-item text=\"All\" [active]=\"activeTab() === 0\">\n <ax-suffix>\n <ax-badge\n [text]=\"totalCount().toString()\"\n [color]=\"activeTab() === 0 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n <ax-tab-item text=\"Unread\" [active]=\"activeTab() === 1\">\n <ax-suffix>\n <ax-badge\n [text]=\"unreadCount().toString()\"\n [color]=\"activeTab() === 1 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n </ax-tabs>\n }\n </div>\n\n <!-- Chat List Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between\" [class.ax-border-t]=\"hasUnread()\">\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-h-full ax-p-4 ax-text-center\">\n <ax-icon class=\"ax-text-danger ax-text-5xl ax-mb-3\">\n <i class=\"fa-light fa-circle-exclamation\"></i>\n </ax-icon>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-error' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-try-again' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-text-secondary ax-mb-4\">{{ error() }}</p>\n <ax-button\n [text]=\"'try-again' | translate: { scope: 'conversation' } | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div class=\"ax-flex-1 ax-overflow-y-auto\">\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n [attr.data-id]=\"i.id\"\n (click)=\"markChatAsRead(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\n ></axm-chat-item>\n }\n\n <!-- Empty State -->\n @empty {\n <div class=\"ax-p-4 ax-font-medium ax-text-center ax-text-secondary\">\n @if (isSearching()) {\n No chats found matching your search\n } @else {\n No chats available\n }\n </div>\n }\n </div>\n }\n\n <!-- Footer Content -->\n <div class=\"ax-border-t ax-border-divider ax-bg-lightest ax-p-4\">\n <ax-button class=\"ax-w-full\" color=\"primary\" text=\"New Conversation\" (onClick)=\"onNewConversation()\">\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-plus\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: AXMChatItemComponent, selector: "axm-chat-item", inputs: ["data"], outputs: ["pressChatItem"] }, { kind: "ngmodule", type:
|
2128
|
+
// Common Modules
|
2129
|
+
CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$3.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type:
|
2130
|
+
// Acorex Core Modules
|
2131
|
+
AXFormatModule }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXCommentModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }, { kind: "directive", type:
|
2132
|
+
// Acorex Component Modules
|
2133
|
+
AXResizableDirective, selector: "[axResizable]", inputs: ["axResizable", "minWidth", "maxWidth", "dblClickAction", "width", "defaultWidth"], outputs: ["axResizableChange", "minWidthChange", "maxWidthChange", "dblClickActionChange", "widthChange", "defaultWidthChange", "onResizingStarted", "onResizingEnded", "onResizingDblClick"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i6$1.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i7$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i7$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
2134
|
+
}
|
2135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatComponent, decorators: [{
|
2136
|
+
type: Component,
|
2137
|
+
args: [{ selector: 'axm-chat', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
2138
|
+
{
|
2139
|
+
provide: AXPPageLayoutBase,
|
2140
|
+
useExisting: AXMChatComponent,
|
2141
|
+
},
|
2142
|
+
AXUnsubscriber,
|
2143
|
+
], imports: [
|
2144
|
+
AXMChatItemComponent,
|
2145
|
+
// Common Modules
|
2146
|
+
CommonModule,
|
2147
|
+
FormsModule,
|
2148
|
+
RouterModule,
|
2149
|
+
// Acorex Core Modules
|
2150
|
+
AXFormatModule,
|
2151
|
+
AXConversationModule,
|
2152
|
+
AXCommentModule,
|
2153
|
+
AXTranslationModule,
|
2154
|
+
// Acorex Component Modules
|
2155
|
+
AXResizableDirective,
|
2156
|
+
AXImageModule,
|
2157
|
+
AXAvatarModule,
|
2158
|
+
AXBadgeModule,
|
2159
|
+
AXDecoratorModule,
|
2160
|
+
AXTextBoxModule,
|
2161
|
+
AXSearchBoxModule,
|
2162
|
+
AXTabsModule,
|
2163
|
+
AXButtonModule,
|
2164
|
+
AXLoadingModule,
|
2165
|
+
AXWysiwygModule,
|
2166
|
+
AXLabelModule,
|
2167
|
+
AXSelectBoxModule,
|
2168
|
+
AXFormModule,
|
2169
|
+
AXDropdownButtonModule,
|
2170
|
+
AXDropdownModule,
|
2171
|
+
AXToolBarModule,
|
2172
|
+
AXSkeletonModule,
|
2173
|
+
AXPPageLayoutComponent,
|
2174
|
+
AXPThemeLayoutBlockComponent,
|
2175
|
+
AXPThemeLayoutStartSideComponent,
|
2176
|
+
AXPThemeLayoutHeaderComponent,
|
2177
|
+
AXPThemeLayoutToolbarComponent,
|
2178
|
+
], template: "<axp-page-layout #container>\n <axp-layout-start-side [axResizable]=\"true\" class=\"ax-border-e ax-lightest-surface ax-h-full\">\n <axp-layout-header>\n <axp-layout-title>{{ 'module-name' | translate: { scope: 'conversation' } | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content class=\"ax-flex ax-flex-col ax-min-h-0\">\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-content style=\"height: calc(100vh - 22rem)\">\n @if (selectedRoom()) {\n <router-outlet></router-outlet>\n } @else if (layoutService.isMobileDevice()) {\n <ax-search-box\n #searchInput\n look=\"solid\"\n [placeholder]=\"placeholder()\"\n [value]=\"searchQuery()\"\n (onValueChanged)=\"onSearch($event.value)\"\n class=\"ax-w-full\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n <ng-container [ngTemplateOutlet]=\"template\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<ng-template #template>\n <!-- Tabs -->\n <div class=\"ax-px-4\">\n @if (hasUnread()) {\n <ax-tabs\n #tab\n class=\"ax-text-neutral-400\"\n [look]=\"'with-line'\"\n [location]=\"'bottom'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"onTabChange($event)\"\n >\n <ax-tab-item text=\"All\" [active]=\"activeTab() === 0\">\n <ax-suffix>\n <ax-badge\n [text]=\"totalCount().toString()\"\n [color]=\"activeTab() === 0 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n <ax-tab-item text=\"Unread\" [active]=\"activeTab() === 1\">\n <ax-suffix>\n <ax-badge\n [text]=\"unreadCount().toString()\"\n [color]=\"activeTab() === 1 ? 'primary' : 'secondary'\"\n class=\"ax-min-w-[1.5rem] ax-justify-center\"\n ></ax-badge>\n </ax-suffix>\n </ax-tab-item>\n </ax-tabs>\n }\n </div>\n\n <!-- Chat List Content -->\n <div class=\"ax-flex-1 ax-overflow-hidden ax-flex ax-flex-col ax-justify-between\" [class.ax-border-t]=\"hasUnread()\">\n <!-- Loading State -->\n @if (isLoading()) {\n <div class=\"ax-p-4 ax-space-y-4\">\n @for (_ of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"ax-flex ax-items-center ax-space-x-3\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-12 ax-h-12 ax-rounded-full\"></ax-skeleton>\n <div class=\"ax-flex-1 ax-space-y-2\">\n <ax-skeleton [animated]=\"true\" class=\"ax-w-3/4 ax-h-4 ax-rounded-md\"></ax-skeleton>\n <ax-skeleton [animated]=\"true\" class=\"ax-w-1/2 ax-h-3 ax-rounded-md\"></ax-skeleton>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Error State -->\n @if (error()) {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-h-full ax-p-4 ax-text-center\">\n <ax-icon class=\"ax-text-danger ax-text-5xl ax-mb-3\">\n <i class=\"fa-light fa-circle-exclamation\"></i>\n </ax-icon>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-error' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-font-semibold ax-text-lg\">\n {{ 'chat-try-again' | translate: { scope: 'conversation' } | async }}\n </p>\n <p class=\"ax-text-secondary ax-mb-4\">{{ error() }}</p>\n <ax-button\n [text]=\"'try-again' | translate: { scope: 'conversation' } | async\"\n color=\"primary\"\n (onClick)=\"refreshChat()\"\n ></ax-button>\n </div>\n }\n\n <!-- Chat List -->\n @if (!isLoading() && !error()) {\n <div class=\"ax-flex-1 ax-overflow-y-auto\">\n @for (i of filteredRooms(); track i.id) {\n <axm-chat-item\n [data]=\"i\"\n [attr.data-id]=\"i.id\"\n (click)=\"markChatAsRead(i.id)\"\n [class.ax-bg-dark]=\"selectedRoom()?.id === i.id\"\n class=\"ax-transition-all ax-duration-100 hover:ax-bg-surface\"\n ></axm-chat-item>\n }\n\n <!-- Empty State -->\n @empty {\n <div class=\"ax-p-4 ax-font-medium ax-text-center ax-text-secondary\">\n @if (isSearching()) {\n No chats found matching your search\n } @else {\n No chats available\n }\n </div>\n }\n </div>\n }\n\n <!-- Footer Content -->\n <div class=\"ax-border-t ax-border-divider ax-bg-lightest ax-p-4\">\n <ax-button class=\"ax-w-full\" color=\"primary\" text=\"New Conversation\" (onClick)=\"onNewConversation()\">\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-solid fa-plus\"></i>\n </ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n</ng-template>\n" }]
|
2179
|
+
}] });
|
2180
|
+
|
2181
|
+
var chat_component = /*#__PURE__*/Object.freeze({
|
2182
|
+
__proto__: null,
|
2183
|
+
AXMChatComponent: AXMChatComponent
|
2184
|
+
});
|
2185
|
+
|
2186
|
+
/**
|
2187
|
+
* Converts chat service response to chat preview format
|
2188
|
+
* @param chatResponse - The response from chat service containing items and total
|
2189
|
+
* @returns Array of formatted chat preview messages
|
2190
|
+
*/
|
2191
|
+
function convertToChatPreview(currentUserId, chatResponse) {
|
2192
|
+
if (!chatResponse || !chatResponse.items?.length) {
|
2193
|
+
return [];
|
2163
2194
|
}
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2167
|
-
|
2168
|
-
|
2169
|
-
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2195
|
+
// Map from AXMChatMessage to ChatPreviewMessage
|
2196
|
+
return chatResponse.items.map((message) => {
|
2197
|
+
// Determine message type based on content type
|
2198
|
+
let messageType = 'text';
|
2199
|
+
// Map contentType to preview type
|
2200
|
+
if (message.message?.contentType) {
|
2201
|
+
switch (message.message.contentType) {
|
2202
|
+
case 'text':
|
2203
|
+
messageType = 'text';
|
2204
|
+
break;
|
2205
|
+
case 'file':
|
2206
|
+
messageType = 'file';
|
2207
|
+
break;
|
2208
|
+
case 'image':
|
2209
|
+
messageType = 'image';
|
2210
|
+
break;
|
2211
|
+
case 'video':
|
2212
|
+
messageType = 'video';
|
2213
|
+
break;
|
2214
|
+
case 'link':
|
2215
|
+
// Determine if link is for audio or other content
|
2216
|
+
const content = message.message.content || '';
|
2217
|
+
if (content.includes('.mp3') || content.includes('.ogg') || content.includes('audio')) {
|
2218
|
+
messageType = 'audio';
|
2219
|
+
}
|
2220
|
+
else {
|
2221
|
+
messageType = 'text';
|
2222
|
+
}
|
2223
|
+
break;
|
2224
|
+
default:
|
2225
|
+
messageType = 'text';
|
2177
2226
|
}
|
2178
2227
|
}
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2186
|
-
|
2187
|
-
|
2228
|
+
// Extract author name if available
|
2229
|
+
const name = message.author ? message.author.id : 'Unknown';
|
2230
|
+
// Create the chat preview message
|
2231
|
+
const previewMessage = {
|
2232
|
+
id: message.id,
|
2233
|
+
sendTime: new Date(message.createdAt || new Date()),
|
2234
|
+
readTime: new Date(), // Assuming "read" when converted
|
2235
|
+
type: messageType,
|
2236
|
+
content: message.message?.content || '',
|
2237
|
+
name: name,
|
2238
|
+
};
|
2239
|
+
// Add fromId if the message is not from current user
|
2240
|
+
if (currentUserId !== message.author.id) {
|
2241
|
+
previewMessage.fromId = message.author?.id;
|
2188
2242
|
}
|
2243
|
+
// Note: Reply information would need to be looked up from the actual messages
|
2244
|
+
// This would typically be done by the service that has access to all messages
|
2245
|
+
return previewMessage;
|
2246
|
+
});
|
2247
|
+
}
|
2248
|
+
/**
|
2249
|
+
* Creates a ChatPreviewMessage from reply information
|
2250
|
+
* @param message - The original message that is being referenced
|
2251
|
+
* @returns A ChatPreviewMessage formatted for use as a replyTo
|
2252
|
+
*/
|
2253
|
+
function createReplyPreview(currentUserId, message) {
|
2254
|
+
if (!message)
|
2255
|
+
return undefined;
|
2256
|
+
return {
|
2257
|
+
id: message.id,
|
2258
|
+
sendTime: new Date(message.createdAt || new Date()),
|
2259
|
+
readTime: new Date(),
|
2260
|
+
type: message.message?.contentType === 'image'
|
2261
|
+
? 'image'
|
2262
|
+
: message.message?.contentType === 'video'
|
2263
|
+
? 'video'
|
2264
|
+
: message.message?.contentType === 'file'
|
2265
|
+
? 'file'
|
2266
|
+
: 'text',
|
2267
|
+
content: message.message?.content || '',
|
2268
|
+
name: message.author?.id || 'Unknown',
|
2269
|
+
fromId: currentUserId !== message.author.id ? message.author?.id : undefined,
|
2270
|
+
};
|
2271
|
+
}
|
2272
|
+
/**
|
2273
|
+
* Generate demo chat preview data with a variety of message types
|
2274
|
+
* @returns Array of sample chat preview messages
|
2275
|
+
*/
|
2276
|
+
function generateDemoChatPreview() {
|
2277
|
+
return [
|
2278
|
+
{
|
2279
|
+
id: '0',
|
2280
|
+
sendTime: new Date(),
|
2281
|
+
type: 'text',
|
2282
|
+
readTime: new Date(),
|
2283
|
+
content: 'Hello John, How are you?',
|
2284
|
+
name: 'test name',
|
2285
|
+
},
|
2286
|
+
{
|
2287
|
+
id: '1',
|
2288
|
+
fromId: '10',
|
2289
|
+
sendTime: new Date(),
|
2290
|
+
readTime: new Date(),
|
2291
|
+
type: 'text',
|
2292
|
+
content: 'Hello',
|
2293
|
+
name: 'test name',
|
2294
|
+
replyTo: {
|
2295
|
+
id: '0',
|
2296
|
+
sendTime: new Date(),
|
2297
|
+
type: 'text',
|
2298
|
+
readTime: new Date(),
|
2299
|
+
content: 'Hello John, How are you?',
|
2300
|
+
name: 'test name',
|
2301
|
+
},
|
2302
|
+
},
|
2303
|
+
{
|
2304
|
+
id: '2',
|
2305
|
+
fromId: '10',
|
2306
|
+
sendTime: new Date(),
|
2307
|
+
readTime: new Date(),
|
2308
|
+
type: 'voice',
|
2309
|
+
name: 'test name',
|
2310
|
+
content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
|
2311
|
+
},
|
2312
|
+
{
|
2313
|
+
id: '3',
|
2314
|
+
sendTime: new Date(),
|
2315
|
+
readTime: new Date(),
|
2316
|
+
type: 'text',
|
2317
|
+
content: 'Can i have your address information?',
|
2318
|
+
name: 'test name',
|
2319
|
+
replyTo: {
|
2320
|
+
id: '1',
|
2321
|
+
fromId: '10',
|
2322
|
+
sendTime: new Date(),
|
2323
|
+
readTime: new Date(),
|
2324
|
+
type: 'text',
|
2325
|
+
content: 'Hello',
|
2326
|
+
name: 'test name',
|
2327
|
+
},
|
2328
|
+
},
|
2329
|
+
{
|
2330
|
+
id: '4',
|
2331
|
+
fromId: '10',
|
2332
|
+
sendTime: new Date(),
|
2333
|
+
readTime: new Date(),
|
2334
|
+
type: 'file',
|
2335
|
+
name: 'test name',
|
2336
|
+
content: `data:audio/webm;codecs=opus;base64,GkXfoEF34fJ`,
|
2337
|
+
},
|
2338
|
+
{
|
2339
|
+
id: '5',
|
2340
|
+
sendTime: new Date(),
|
2341
|
+
readTime: new Date(),
|
2342
|
+
type: 'image',
|
2343
|
+
name: 'test name',
|
2344
|
+
content: `https://picsum.photos/300/200`,
|
2345
|
+
},
|
2346
|
+
{
|
2347
|
+
id: '6',
|
2348
|
+
sendTime: new Date(),
|
2349
|
+
readTime: new Date(),
|
2350
|
+
type: 'video',
|
2351
|
+
name: 'test name',
|
2352
|
+
content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
|
2353
|
+
},
|
2354
|
+
{
|
2355
|
+
id: '7',
|
2356
|
+
sendTime: new Date(),
|
2357
|
+
type: 'text',
|
2358
|
+
readTime: new Date(),
|
2359
|
+
content: 'Hello John, How are you?',
|
2360
|
+
name: 'test name',
|
2361
|
+
},
|
2362
|
+
{
|
2363
|
+
id: '8',
|
2364
|
+
fromId: '10',
|
2365
|
+
sendTime: new Date(),
|
2366
|
+
readTime: new Date(),
|
2367
|
+
type: 'audio',
|
2368
|
+
name: 'kids',
|
2369
|
+
content: `https://actions.google.com/sounds/v1/ambiences/kids_playing.ogg`,
|
2370
|
+
},
|
2371
|
+
{
|
2372
|
+
id: '9',
|
2373
|
+
fromId: '10',
|
2374
|
+
sendTime: new Date(),
|
2375
|
+
readTime: new Date(),
|
2376
|
+
type: 'image',
|
2377
|
+
name: 'test name',
|
2378
|
+
content: `https://picsum.photos/300/200`,
|
2379
|
+
},
|
2380
|
+
{
|
2381
|
+
id: '10',
|
2382
|
+
fromId: '10',
|
2383
|
+
sendTime: new Date(),
|
2384
|
+
readTime: new Date(),
|
2385
|
+
type: 'image',
|
2386
|
+
name: 'test name',
|
2387
|
+
content: `https://picsum.photos/200/300`,
|
2388
|
+
},
|
2389
|
+
{
|
2390
|
+
id: '11',
|
2391
|
+
fromId: '10',
|
2392
|
+
sendTime: new Date(),
|
2393
|
+
readTime: new Date(),
|
2394
|
+
type: 'video',
|
2395
|
+
name: 'test name',
|
2396
|
+
content: `https://www.pexels.com/download/video/5495322/?fps=29.97&h=540&w=960`,
|
2397
|
+
},
|
2398
|
+
{
|
2399
|
+
id: '12',
|
2400
|
+
sendTime: new Date(),
|
2401
|
+
readTime: new Date(),
|
2402
|
+
type: 'file',
|
2403
|
+
name: 'test name',
|
2404
|
+
content: `data:audio/webm;codecs=opus;base64,GkXfo59ChoE+u5BxHVL7ZAS1EF34fJ`,
|
2405
|
+
},
|
2406
|
+
{
|
2407
|
+
id: '13',
|
2408
|
+
sendTime: new Date(),
|
2409
|
+
readTime: new Date(),
|
2410
|
+
type: 'audio',
|
2411
|
+
name: 'alarm',
|
2412
|
+
content: `https://actions.google.com/sounds/v1/alarms/digital_watch_alarm_long.ogg`,
|
2413
|
+
},
|
2414
|
+
];
|
2415
|
+
}
|
2416
|
+
/**
|
2417
|
+
* Usage Examples:
|
2418
|
+
*
|
2419
|
+
* Example 1: Converting chat service response to chat preview format
|
2420
|
+
* ```typescript
|
2421
|
+
* import { AXMChatService } from '../chat.service';
|
2422
|
+
* import { convertToChatPreview } from './chat-preview.helper';
|
2423
|
+
*
|
2424
|
+
* export class ChatPreviewComponent {
|
2425
|
+
* constructor(private chatService: AXMChatService) {}
|
2426
|
+
*
|
2427
|
+
* async loadChatMessages(roomId: string) {
|
2428
|
+
* const response = await this.chatService.getChatMessages(roomId);
|
2429
|
+
*
|
2430
|
+
* // Convert to chat preview format
|
2431
|
+
* const previewMessages = convertToChatPreview(response);
|
2432
|
+
*
|
2433
|
+
* // Use the converted messages
|
2434
|
+
* this.conversationService.chats.set(previewMessages);
|
2435
|
+
* }
|
2436
|
+
* }
|
2437
|
+
* ```
|
2438
|
+
*
|
2439
|
+
* Example 2: Using demo data for development/testing
|
2440
|
+
* ```typescript
|
2441
|
+
* import { generateDemoChatPreview } from './chat-preview.helper';
|
2442
|
+
*
|
2443
|
+
* export class ChatPreviewDemoComponent {
|
2444
|
+
* ngOnInit() {
|
2445
|
+
* // Load demo messages
|
2446
|
+
* const demoMessages = generateDemoChatPreview();
|
2447
|
+
*
|
2448
|
+
* // Use the demo messages
|
2449
|
+
* this.conversationService.chats.set(demoMessages);
|
2450
|
+
* }
|
2451
|
+
* }
|
2452
|
+
*/
|
2453
|
+
|
2454
|
+
class AXMChatPreviewComponent {
|
2455
|
+
constructor() {
|
2456
|
+
this.activatedRoute = inject(ActivatedRoute);
|
2457
|
+
this.fileService = inject(AXFileService);
|
2458
|
+
this.chatService = inject(AXMChatService);
|
2459
|
+
this.sessionService = inject(AXPSessionService);
|
2460
|
+
this.toastService = inject(AXToastService);
|
2461
|
+
this.chatData = signal([]);
|
2462
|
+
this.isEditing = signal(false);
|
2463
|
+
this.editId = signal(null);
|
2464
|
+
this.height = signal(0);
|
2465
|
+
this.isLoading = signal(false);
|
2466
|
+
this.error = signal(null);
|
2467
|
+
this.options = signal({
|
2468
|
+
disabled: false,
|
2469
|
+
readonly: false,
|
2470
|
+
value: '',
|
2471
|
+
});
|
2472
|
+
this.roomId = '';
|
2189
2473
|
}
|
2190
|
-
|
2191
|
-
this.
|
2192
|
-
|
2193
|
-
|
2194
|
-
|
2195
|
-
|
2196
|
-
|
2197
|
-
|
2198
|
-
|
2199
|
-
|
2200
|
-
? reply.reactionsCount - 1
|
2201
|
-
: (reply.reactionsCount || 0) + 1,
|
2202
|
-
}
|
2203
|
-
: reply);
|
2204
|
-
return {
|
2205
|
-
...comment,
|
2206
|
-
replies: updatedReplies,
|
2207
|
-
};
|
2208
|
-
}
|
2209
|
-
else {
|
2210
|
-
return {
|
2211
|
-
...comment,
|
2212
|
-
isLiked: !comment.isLiked,
|
2213
|
-
reactionsCount: comment.isLiked && comment.reactionsCount !== undefined
|
2214
|
-
? comment.reactionsCount - 1
|
2215
|
-
: (comment.reactionsCount || 0) + 1,
|
2216
|
-
};
|
2217
|
-
}
|
2218
|
-
}
|
2219
|
-
return comment;
|
2220
|
-
});
|
2474
|
+
setHeight(height) {
|
2475
|
+
this.height.set(height);
|
2476
|
+
}
|
2477
|
+
ngOnInit() {
|
2478
|
+
this.activatedRoute.params.subscribe((params) => {
|
2479
|
+
this.roomId = params['id'];
|
2480
|
+
this.loadMessages(this.roomId);
|
2481
|
+
// setTimeout(() => {
|
2482
|
+
// this.scrollToEnd();
|
2483
|
+
// }, 1000);
|
2221
2484
|
});
|
2222
2485
|
}
|
2223
|
-
|
2224
|
-
|
2225
|
-
|
2226
|
-
|
2227
|
-
|
2228
|
-
|
2229
|
-
|
2230
|
-
|
2231
|
-
|
2232
|
-
|
2233
|
-
|
2234
|
-
|
2486
|
+
handleFileChange(event) {
|
2487
|
+
console.log('File Changed:', event);
|
2488
|
+
}
|
2489
|
+
handleCancelRecord(event) {
|
2490
|
+
console.log('Recording Cancelled:', event);
|
2491
|
+
}
|
2492
|
+
handleEndRecord(event) {
|
2493
|
+
this.fileService.blobToBase64(event.data.value).then((base64Content) => {
|
2494
|
+
// Create a properly formatted chat preview message for voice
|
2495
|
+
const newVoiceMessage = {
|
2496
|
+
id: `${Math.floor(Math.random() * 10000)}`,
|
2497
|
+
content: base64Content,
|
2498
|
+
sendTime: new Date(),
|
2499
|
+
readTime: new Date(),
|
2500
|
+
type: 'voice',
|
2501
|
+
name: 'test name',
|
2502
|
+
};
|
2503
|
+
this.chatData.update((values) => [...values, newVoiceMessage]);
|
2504
|
+
});
|
2505
|
+
}
|
2506
|
+
handleOnSend(e) {
|
2507
|
+
if (this.isEditing() && this.editId()) {
|
2508
|
+
this.chatService
|
2509
|
+
.editMessage(this.editId(), e.data.value, e.data.type || 'text')
|
2510
|
+
.then((newMessage) => {
|
2511
|
+
const convertedNewMessage = convertToChatPreview(this.sessionService.user?.id || '', {
|
2512
|
+
items: [newMessage],
|
2513
|
+
total: 1,
|
2514
|
+
})[0];
|
2515
|
+
this.chatData.update((values) => values.map((item) => (item.id === this.editId() ? { ...convertedNewMessage } : item)));
|
2516
|
+
this.editId.set(null);
|
2517
|
+
this.options.update((prev) => ({ ...prev, value: '' }));
|
2518
|
+
this.isEditing.set(false);
|
2519
|
+
})
|
2520
|
+
.catch((error) => {
|
2521
|
+
this.toastService.danger('Failed to edit message');
|
2522
|
+
});
|
2235
2523
|
}
|
2236
2524
|
else {
|
2237
|
-
|
2238
|
-
|
2525
|
+
if (e.data.value && this.roomId) {
|
2526
|
+
const replyId = e.data.replyChat?.id;
|
2527
|
+
this.chatService
|
2528
|
+
.sendMessage(this.roomId, e.data.value, e.data.type || 'text', replyId)
|
2529
|
+
.then((newMessage) => {
|
2530
|
+
const convertedNewMessage = convertToChatPreview(this.sessionService.user?.id || '', {
|
2531
|
+
items: [newMessage],
|
2532
|
+
total: 1,
|
2533
|
+
})[0];
|
2534
|
+
this.chatData.update((values) => [...values, { ...convertedNewMessage, replyTo: replyId }]);
|
2535
|
+
this.options.update((prev) => ({ ...prev, value: '' }));
|
2536
|
+
this.scrollToEnd();
|
2537
|
+
})
|
2538
|
+
.catch((error) => {
|
2539
|
+
this.toastService.danger('Failed to send message');
|
2540
|
+
});
|
2541
|
+
}
|
2239
2542
|
}
|
2240
2543
|
}
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
|
2245
|
-
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2251
|
-
draggable: true,
|
2252
|
-
hasBackdrop: true,
|
2253
|
-
closeButton: true,
|
2254
|
-
closeOnBackdropClick: false,
|
2255
|
-
};
|
2256
|
-
const popup = await this.popupService.open(AXMCommentLookupPopup, popupConfig);
|
2257
|
-
memberLookup = popup.data?.data?.lookup;
|
2544
|
+
handleOnAction(e) {
|
2545
|
+
console.log('Action Triggered:', e);
|
2546
|
+
}
|
2547
|
+
scrollToEnd() {
|
2548
|
+
const conversationView = document.querySelector('ax-conversation-view');
|
2549
|
+
if (conversationView) {
|
2550
|
+
conversationView.scrollTo({
|
2551
|
+
top: conversationView.scrollHeight,
|
2552
|
+
behavior: 'smooth',
|
2553
|
+
});
|
2258
2554
|
}
|
2555
|
+
}
|
2556
|
+
async loadMessages(roomId) {
|
2557
|
+
this.isLoading.set(true);
|
2558
|
+
this.error.set(null);
|
2259
2559
|
try {
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2267
|
-
|
2268
|
-
this.hasCooldown.set(true);
|
2269
|
-
this.isEditingMode.set(false);
|
2270
|
-
this.activeEditComment.set(undefined);
|
2271
|
-
}
|
2272
|
-
else {
|
2273
|
-
const payload = {
|
2274
|
-
...this.payload(),
|
2275
|
-
content: this.commentContent(),
|
2276
|
-
contentType: 'text',
|
2277
|
-
isPrivate: isPrivate,
|
2278
|
-
replyId: this.activeReplyComment()?.id ?? null,
|
2279
|
-
};
|
2280
|
-
await this.commentService.insertOne(payload);
|
2281
|
-
this.hasCooldown.set(true);
|
2282
|
-
this.isReplyingMode.set(false);
|
2283
|
-
this.activeReplyComment.set(undefined);
|
2284
|
-
}
|
2285
|
-
// Reload comments to get the updated list with proper hierarchy
|
2286
|
-
const response = await this.commentService.query(this.getPayload());
|
2287
|
-
this.comments.set(response.items);
|
2288
|
-
this.commentContent.set('');
|
2289
|
-
document.getElementsByClassName('ql-editor')[0].innerHTML = '';
|
2290
|
-
setTimeout(() => {
|
2291
|
-
this.hasCooldown.set(false);
|
2292
|
-
}, 1000);
|
2560
|
+
// Get messages from the chat service
|
2561
|
+
const messageResponse = await this.chatService.getMessages(roomId);
|
2562
|
+
// Convert to chat preview format
|
2563
|
+
const chatPreviewMessages = convertToChatPreview(this.sessionService.user?.id || '', messageResponse).sort((a, b) => {
|
2564
|
+
return new Date(a.sendTime).getTime() - new Date(b.sendTime).getTime();
|
2565
|
+
});
|
2566
|
+
// Set the messages in the conversation service
|
2567
|
+
this.chatData.set(chatPreviewMessages);
|
2293
2568
|
}
|
2294
2569
|
catch (error) {
|
2295
|
-
|
2296
|
-
|
2297
|
-
content: typeof error === 'string' ? error : 'Failed to submit comment. Please try again.',
|
2298
|
-
color: 'danger',
|
2299
|
-
location: 'bottom-center',
|
2300
|
-
closeButton: true,
|
2301
|
-
timeOut: 3000,
|
2302
|
-
timeOutProgress: true,
|
2303
|
-
});
|
2570
|
+
this.error.set('Failed to load messages. Please try again.');
|
2571
|
+
console.error('Failed to load messages:', error);
|
2304
2572
|
}
|
2305
2573
|
finally {
|
2306
|
-
this.
|
2574
|
+
this.isLoading.set(false);
|
2307
2575
|
}
|
2308
2576
|
}
|
2309
|
-
|
2310
|
-
|
2311
|
-
|
2312
|
-
|
2313
|
-
if (el) {
|
2314
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
2315
|
-
const content = el?.firstElementChild?.children[1];
|
2316
|
-
if (content) {
|
2317
|
-
const prevBg = content.style.background;
|
2318
|
-
content.style.borderRadius = '0.25rem';
|
2319
|
-
content.style.transition = 'background 1s ease-in-out';
|
2320
|
-
content.style.background = `rgba(var(--ax-color-on-surface), var(--tw-bg-opacity))`;
|
2321
|
-
setTimeout(() => {
|
2322
|
-
content.style.background = prevBg || 'rgba(0, 0, 0, 0)';
|
2323
|
-
}, 1000);
|
2324
|
-
}
|
2325
|
-
}
|
2577
|
+
addItemHandler(e) {
|
2578
|
+
if (e.data.fromId) {
|
2579
|
+
e.canceled = true;
|
2580
|
+
return;
|
2326
2581
|
}
|
2582
|
+
const userItem = [];
|
2583
|
+
if (e.data.type === 'text') {
|
2584
|
+
userItem.push({
|
2585
|
+
text: 'Edit',
|
2586
|
+
color: 'primary',
|
2587
|
+
icon: 'fa-solid fa-pencil',
|
2588
|
+
onClick: (id) => {
|
2589
|
+
try {
|
2590
|
+
this.isEditing.set(true);
|
2591
|
+
this.editId.set(e.data.id);
|
2592
|
+
this.options.update((prev) => ({ ...prev, value: e.data.content }));
|
2593
|
+
}
|
2594
|
+
catch (error) {
|
2595
|
+
this.toastService.danger('Failed to edit message');
|
2596
|
+
}
|
2597
|
+
},
|
2598
|
+
});
|
2599
|
+
}
|
2600
|
+
if (e.data.type === 'text') {
|
2601
|
+
userItem.push({
|
2602
|
+
text: 'Delete',
|
2603
|
+
color: 'danger',
|
2604
|
+
icon: 'fa-solid fa-trash-can',
|
2605
|
+
onClick: (id) => {
|
2606
|
+
try {
|
2607
|
+
this.chatService.deleteMessage(id).then((res) => {
|
2608
|
+
this.chatData.update((values) => values.filter((item) => item.id !== id));
|
2609
|
+
if (!res) {
|
2610
|
+
this.toastService.danger('Failed to delete message');
|
2611
|
+
}
|
2612
|
+
});
|
2613
|
+
}
|
2614
|
+
catch (error) {
|
2615
|
+
this.toastService.danger('Failed to delete message');
|
2616
|
+
}
|
2617
|
+
},
|
2618
|
+
});
|
2619
|
+
}
|
2620
|
+
e.items.push(...userItem);
|
2327
2621
|
}
|
2328
|
-
|
2329
|
-
|
2330
|
-
}
|
2331
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
2332
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", 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 }); }
|
2622
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
2623
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMChatPreviewComponent, isStandalone: true, selector: "axm-chat-preview", ngImport: i0, template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n [chatBoxHeight]=\"height() - 60 + 'px'\"\n ></ax-conversation-view>\n <ax-conversation-input\n class=\"ax-p-1\"\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n ></ax-conversation-input>\n </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDomChangeDirective, selector: "[axDomChange]", outputs: ["axMutationObserver", "axResizeObserver"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "component", type: i2$4.AXConversationViewComponent, selector: "ax-conversation-view", inputs: ["chatBoxHeight", "isReplyArrowShown"], outputs: ["onScrollEnd", "onActionMenuOpening", "onAction"] }, { kind: "component", type: i2$4.AXConversationInputComponent, selector: "ax-conversation-input", inputs: ["look", "placeholder", "maxLength", "hasAttachment", "hasVoice", "hasEmoji", "isLoading", "acceptFileType"], outputs: ["onSendClick", "onStartRecording", "onCancelRecording", "onEnterPressed"] }, { kind: "component", type: i2$4.AXConversationContainerComponent, selector: "ax-conversation-container", inputs: ["chatData"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
2333
2624
|
}
|
2334
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type:
|
2625
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMChatPreviewComponent, decorators: [{
|
2335
2626
|
type: Component,
|
2336
|
-
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"] }]
|
2627
|
+
args: [{ selector: 'axm-chat-preview', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, AXDomChangeDirective, AXConversationModule, AXLoadingModule], template: "@if (isLoading()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n} @else if (error()) {\n <div class=\"ax-h-full ax-w-full ax-flex ax-items-center ax-justify-center\">\n <div class=\"ax-text-center\">\n <p class=\"ax-text-danger\">{{ error() }}</p>\n <button (click)=\"loadMessages(roomId)\" class=\"ax-mt-2 ax-button ax-primary\">Retry</button>\n </div>\n </div>\n} @else {\n <div\n axDomChange\n (axResizeObserver)=\"setHeight($event[0].contentRect.height)\"\n class=\"ax-bg-surface-container ax-h-full\"\n >\n <!-- Messages Container -->\n <ax-conversation-container class=\"ax-overflow-hidden\" [chatData]=\"chatData()\">\n <ax-conversation-view\n (onActionMenuOpening)=\"addItemHandler($event)\"\n (onAction)=\"handleOnAction($event)\"\n [chatBoxHeight]=\"height() - 60 + 'px'\"\n ></ax-conversation-view>\n <ax-conversation-input\n class=\"ax-p-1\"\n placeholder=\"Type a message...\"\n [(ngModel)]=\"options().value\"\n (onSendClick)=\"handleOnSend($event)\"\n (onFileChange)=\"handleFileChange($event)\"\n (onStopRecording)=\"handleEndRecord($event)\"\n (onCancelRecording)=\"handleCancelRecord($event)\"\n ></ax-conversation-input>\n </ax-conversation-container>\n </div>\n}\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
|
2337
2628
|
}] });
|
2338
2629
|
|
2630
|
+
var chatPreview_component = /*#__PURE__*/Object.freeze({
|
2631
|
+
__proto__: null,
|
2632
|
+
AXMChatPreviewComponent: AXMChatPreviewComponent
|
2633
|
+
});
|
2634
|
+
|
2339
2635
|
class AXMCommentWidgetViewComponent extends AXPLayoutWidgetComponent {
|
2340
2636
|
constructor() {
|
2341
2637
|
super(...arguments);
|
@@ -2748,7 +3044,7 @@ class AXMCommentWidgetViewComponent extends AXPLayoutWidgetComponent {
|
|
2748
3044
|
return date ? Date.now() - date.getTime() : undefined;
|
2749
3045
|
}
|
2750
3046
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
2751
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentWidgetViewComponent, isStandalone: true, selector: "axm-comment-widget-view", viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }, { propertyName: "wysiwyg", first: true, predicate: ["w"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-mt-2 ax-p-2\">\n <ax-comment-container>\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (checkImageExists(comment.id!) && comment.author && extractInitials(comment.author) !== '?') {\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(comment.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(comment.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (reply && checkImageExists(reply.id!) && reply.author && extractInitials(reply.author) !== '?') {\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(reply.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(reply.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteReply(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <ax-form>\n <ax-form-field>\n <div>\n @if (isReplyingMode() || isEditingMode()) {\n <div\n class=\"ax-flex ax-justify-between ax-rounded-b-none ax-border ax-border-b-0 ax-rounded-lg ax-bg-on-surface ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\"\n >\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i\n [class]=\"isReplyingMode() ? 'fa-reply' : 'fa-pen'\"\n class=\"fa-solid ax-text-primary-500 dark:ax-text-primary-300 ax-text-2xl ax-me-4\"\n ></i>\n <div\n class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\"\n >\n <p class=\"ax-text-primary-500 dark:ax-text-primary-300\">\n {{ isReplyingMode() ? 'Reply to ' : 'Edit Message' }}\n <span class=\"ax-font-bold\">\n {{ isReplyingMode() ? extractInitials(activeReplyComment()?.author) : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-toolbar>\n <ax-content>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-content>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown()\"\n type=\"submit\"\n color=\"primary\"\n mode=\"split\"\n text=\"Send\"\n (onClick)=\"submitComment()\"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n <ax-button-item-list>\n <ax-button-item (click)=\"submitComment(true)\" text=\"Send Private ...\" name=\"private\" data=\"private\">\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-user-secret\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </ax-suffix>\n </ax-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>\n", styles: ["ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}\n"], dependencies: [{ kind: "component", type: AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i2.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i13.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXCommentModule }, { 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: "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"] }, { 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: "ngmodule", type: AXImageModule }, { kind: "component", type: i1.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i5$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i11$1.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: 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: "ngmodule", type: AXToolBarModule }, { kind: "component", type: i12.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "ngmodule", type: AXDropdownButtonModule }, { 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: "ngmodule", type: AXValidationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i9.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i9.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i9.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i15.AXFormatPipe, name: "format" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
3047
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: AXMCommentWidgetViewComponent, isStandalone: true, selector: "axm-comment-widget-view", viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }, { propertyName: "wysiwyg", first: true, predicate: ["w"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-mt-2 ax-p-2\">\n <ax-comment-container>\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-py-12 ax-bg-lightest ax-px-5\">\n <ax-skeleton class=\"ax-min-w-16 ax-h-16 ax-rounded-full ax-me-4\"></ax-skeleton>\n <div class=\"ax-flex ax-flex-col ax-gap-2 ax-w-full\">\n <ax-skeleton class=\"ax-w-full ax-h-6 ax-rounded\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-full ax-h-2 ax-rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"ax-w-8/12 ax-h-2 ax-rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (!isLoading() && comments().length > 0) {\n <ax-comment-view class=\"ax-bg-lightest\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item [id]=\"comment.id!\" [replyCount]=\"comment.replies?.length ?? 0\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (checkImageExists(comment.id!) && comment.author && extractInitials(comment.author) !== '?') {\n <ax-image (onError)=\"handleImageError(comment.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(comment.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(comment.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(comment.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteComment(comment)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(comment.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment)\" [liked]=\"comment.isLiked!\">\n {{ comment.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment)\"></ax-comment-reply-text>\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item [id]=\"reply.id\">\n <ax-avatar [color]=\"avatarConfig().color\" [shape]=\"avatarConfig().look\">\n @if (reply && checkImageExists(reply.id!) && reply.author && extractInitials(reply.author) !== '?') {\n <ax-image (onError)=\"handleImageError(reply.id!)\" [src]=\"''\">\n <ax-loading></ax-loading>\n </ax-image>\n } @else {\n <ax-text>\n <span class=\"ax-text-base ax-overflow-hidden\">{{ extractInitials(reply.author) }}</span>\n </ax-text>\n }\n </ax-avatar>\n <ax-title>{{ extractInitials(reply.author) }}</ax-title>\n <ax-comment-date>{{ calcDefrenetTime(reply.createdAt) | format: 'timeleft' | async }} </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button class=\"ax-sm\" look=\"blank\" color=\"neutral\">\n <ax-icon icon=\"ax-icon ax-icon-solid ax-icon-more-horizontal\"></ax-icon>\n </ax-button>\n\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item text=\"Edit\" color=\"neutral\" (click)=\"editMessage(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-edit\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item text=\"Delete\" color=\"danger\" (click)=\"deleteReply(comment, reply)\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content [innerHTML]=\"sanitizeHtml(reply.message.content)\"></ax-content>\n <ax-comment-like (click)=\"toggleLike(comment, reply)\" [liked]=\"reply.isLiked!\">\n {{ reply.reactionsCount }}\n </ax-comment-like>\n <ax-comment-reply-text (click)=\"replyMessage(comment, reply)\"></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n } @else {\n <div>\n <div class=\"ax-flex ax-flex-col ax-gap-4 ax-justify-center ax-items-center ax-p-10\">\n <svg\n class=\"ax-mx-auto\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"154\"\n height=\"161\"\n viewBox=\"0 0 154 161\"\n fill=\"none\"\n >\n <path\n d=\"M0.0616455 84.4268C0.0616455 42.0213 34.435 7.83765 76.6507 7.83765C118.803 7.83765 153.224 42.0055 153.224 84.4268C153.224 102.42 147.026 118.974 136.622 132.034C122.282 150.138 100.367 161 76.6507 161C52.7759 161 30.9882 150.059 16.6633 132.034C6.25961 118.974 0.0616455 102.42 0.0616455 84.4268Z\"\n fill=\"#EEF2FF\"\n />\n <path\n d=\"M96.8189 0.632498L96.8189 0.632384L96.8083 0.630954C96.2034 0.549581 95.5931 0.5 94.9787 0.5H29.338C22.7112 0.5 17.3394 5.84455 17.3394 12.4473V142.715C17.3394 149.318 22.7112 154.662 29.338 154.662H123.948C130.591 154.662 135.946 149.317 135.946 142.715V38.9309C135.946 38.0244 135.847 37.1334 135.648 36.2586L135.648 36.2584C135.117 33.9309 133.874 31.7686 132.066 30.1333C132.066 30.1331 132.065 30.1329 132.065 30.1327L103.068 3.65203C103.068 3.6519 103.067 3.65177 103.067 3.65164C101.311 2.03526 99.1396 0.995552 96.8189 0.632498Z\"\n fill=\"white\"\n stroke=\"#E5E7EB\"\n />\n <ellipse cx=\"80.0618\" cy=\"81\" rx=\"28.0342\" ry=\"28.0342\" fill=\"#EEF2FF\" />\n <path\n d=\"M99.2393 61.3061L99.2391 61.3058C88.498 50.5808 71.1092 50.5804 60.3835 61.3061C49.6423 72.0316 49.6422 89.4361 60.3832 100.162C71.109 110.903 88.4982 110.903 99.2393 100.162C109.965 89.4363 109.965 72.0317 99.2393 61.3061ZM105.863 54.6832C120.249 69.0695 120.249 92.3985 105.863 106.785C91.4605 121.171 68.1468 121.171 53.7446 106.785C39.3582 92.3987 39.3582 69.0693 53.7446 54.683C68.1468 40.2965 91.4605 40.2966 105.863 54.6832Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M110.782 119.267L102.016 110.492C104.888 108.267 107.476 105.651 109.564 102.955L118.329 111.729L110.782 119.267Z\"\n stroke=\"#E5E7EB\"\n />\n <path\n d=\"M139.122 125.781L139.122 125.78L123.313 109.988C123.313 109.987 123.313 109.987 123.312 109.986C121.996 108.653 119.849 108.657 118.521 109.985L118.871 110.335L118.521 109.985L109.047 119.459C107.731 120.775 107.735 122.918 109.044 124.247L109.047 124.249L124.858 140.06C128.789 143.992 135.191 143.992 139.122 140.06C143.069 136.113 143.069 129.728 139.122 125.781Z\"\n fill=\"#A5B4FC\"\n stroke=\"#818CF8\"\n />\n <path\n d=\"M83.185 87.2285C82.5387 87.2285 82.0027 86.6926 82.0027 86.0305C82.0027 83.3821 77.9987 83.3821 77.9987 86.0305C77.9987 86.6926 77.4627 87.2285 76.8006 87.2285C76.1543 87.2285 75.6183 86.6926 75.6183 86.0305C75.6183 80.2294 84.3831 80.2451 84.3831 86.0305C84.3831 86.6926 83.8471 87.2285 83.185 87.2285Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M93.3528 77.0926H88.403C87.7409 77.0926 87.2049 76.5567 87.2049 75.8946C87.2049 75.2483 87.7409 74.7123 88.403 74.7123H93.3528C94.0149 74.7123 94.5509 75.2483 94.5509 75.8946C94.5509 76.5567 94.0149 77.0926 93.3528 77.0926Z\"\n fill=\"#4F46E5\"\n />\n <path\n d=\"M71.5987 77.0925H66.6488C65.9867 77.0925 65.4507 76.5565 65.4507 75.8945C65.4507 75.2481 65.9867 74.7122 66.6488 74.7122H71.5987C72.245 74.7122 72.781 75.2481 72.781 75.8945C72.781 76.5565 72.245 77.0925 71.5987 77.0925Z\"\n fill=\"#4F46E5\"\n />\n <rect x=\"38.3522\" y=\"21.5128\" width=\"41.0256\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <rect x=\"38.3522\" y=\"133.65\" width=\"54.7009\" height=\"5.47009\" rx=\"2.73504\" fill=\"#A5B4FC\" />\n <rect x=\"38.3522\" y=\"29.7179\" width=\"13.6752\" height=\"2.73504\" rx=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"56.13\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"61.6001\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n <circle cx=\"67.0702\" cy=\"31.0854\" r=\"1.36752\" fill=\"#4F46E5\" />\n </svg>\n <div>\n <h2 class=\"ax-text-center ax-text-neutral-600 ax-font-semibold ax-leading-loose ax-pb-2\">\n There is no Comment!\n </h2>\n </div>\n </div>\n </div>\n }\n </ax-comment-container>\n <ax-form>\n <ax-form-field>\n <div>\n @if (isReplyingMode() || isEditingMode()) {\n <div\n class=\"ax-flex ax-justify-between ax-rounded-b-none ax-border ax-border-b-0 ax-rounded-lg ax-bg-on-surface ax-px-6 ax-py-3 ax-w-full ax-items-center ax-overflow-hidden ax-text-sm ax-leading-none\"\n >\n <div (click)=\"scrollMain()\" class=\"ax-flex ax-justify-start ax-items-center ax-cursor-pointer\">\n <i\n [class]=\"isReplyingMode() ? 'fa-reply' : 'fa-pen'\"\n class=\"fa-solid ax-text-primary-500 dark:ax-text-primary-300 ax-text-2xl ax-me-4\"\n ></i>\n <div\n class=\"ax-flex ax-flex-col ax-gap-2 ax-justify-between ax-align-middle ax-leading-4 ax-overflow-hidden\"\n >\n <p class=\"ax-text-primary-500 dark:ax-text-primary-300\">\n {{ isReplyingMode() ? 'Reply to ' : 'Edit Message' }}\n <span class=\"ax-font-bold\">\n {{ isReplyingMode() ? extractInitials(activeReplyComment()?.author) : '' }}\n </span>\n </p>\n <div\n class=\"ax-truncate\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(activeReplyComment()?.message?.content ?? '')\n : sanitizeHtml(activeEditComment()?.message?.content ?? '')\n \"\n ></div>\n </div>\n </div>\n <div><i (click)=\"resetReplyEditState()\" class=\"fa-solid ax-text-2xl fa-xmark ax-cursor-pointer\"></i></div>\n </div>\n }\n <ax-wysiwyg-container #w [look]=\"wysiwygOptions().look\" [(ngModel)]=\"commentContent\">\n <ax-wysiwyg-view class=\"ax-min-h-28\"></ax-wysiwyg-view>\n <ax-toolbar>\n <ax-content>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-content>\n <ax-suffix>\n <ax-dropdown-button\n [disabled]=\"hasCooldown()\"\n type=\"submit\"\n color=\"primary\"\n mode=\"split\"\n text=\"Send\"\n (onClick)=\"submitComment()\"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n <ax-button-item-list>\n <ax-button-item (click)=\"submitComment(true)\" text=\"Send Private ...\" name=\"private\" data=\"private\">\n <ax-prefix>\n <ax-icon icon=\"fa-regular fa-user-secret\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-button>\n </ax-suffix>\n </ax-toolbar>\n <ax-validation-rule rule=\"callback\" [options]=\"{ validate: validateContent }\"></ax-validation-rule>\n </ax-wysiwyg-container>\n </div>\n </ax-form-field>\n </ax-form>\n</div>\n", styles: ["ax-wysiwyg-container .ax-editor-container{border-top-left-radius:0!important;border-top-right-radius:0!important}ax-wysiwyg-container .ax-error-message{padding-left:.5rem}\n"], dependencies: [{ kind: "component", type: AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i6.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXConversationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4.AxCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount"] }, { kind: "component", type: i4.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["liked"], outputs: ["likedChange", "onLiked"] }, { kind: "component", type: i4.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i5$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i5$1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i5$1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i7.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i8.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i9.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i5.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i5.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "component", type: i1.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "component", type: i12.AXToolBarComponent, selector: "ax-toolbar" }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: i14.AXDropdownButtonComponent, selector: "ax-dropdown-button", inputs: ["disabled", "size", "color", "look", "text", "type", "mode"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "lookChange", "colorChange", "disabledChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i11.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i11.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i11.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i10.AXFormatPipe, name: "format" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
2752
3048
|
}
|
2753
3049
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMCommentWidgetViewComponent, decorators: [{
|
2754
3050
|
type: Component,
|
@@ -2796,74 +3092,38 @@ const AXPCommentWidget = {
|
|
2796
3092
|
},
|
2797
3093
|
};
|
2798
3094
|
|
2799
|
-
|
2800
|
-
|
2801
|
-
|
2802
|
-
|
2803
|
-
|
2804
|
-
{
|
2805
|
-
|
2806
|
-
component: AXMChatComponent,
|
2807
|
-
children: [
|
2808
|
-
{
|
2809
|
-
path: ':id',
|
2810
|
-
component: AXMChatPreviewComponent,
|
2811
|
-
},
|
2812
|
-
],
|
2813
|
-
},
|
2814
|
-
{
|
2815
|
-
path: 'comments/m/:module/e/:id',
|
2816
|
-
component: AXMCommentListViewComponent,
|
3095
|
+
function routesFactory() {
|
3096
|
+
const routes = [
|
3097
|
+
{
|
3098
|
+
path: ':app/chat',
|
3099
|
+
canActivate: [AXPAuthGuard],
|
3100
|
+
loadComponent: () => {
|
3101
|
+
return AXPRootLayoutComponent;
|
2817
3102
|
},
|
2818
|
-
|
2819
|
-
|
2820
|
-
|
3103
|
+
children: [
|
3104
|
+
{
|
3105
|
+
path: '',
|
3106
|
+
loadComponent: () => Promise.resolve().then(function () { return chat_component; }).then((c) => c.AXMChatComponent),
|
3107
|
+
data: { reuse: true },
|
3108
|
+
children: [
|
3109
|
+
{
|
3110
|
+
path: ':id',
|
3111
|
+
loadComponent: () => Promise.resolve().then(function () { return chatPreview_component; }).then((c) => c.AXMChatPreviewComponent),
|
3112
|
+
},
|
3113
|
+
],
|
3114
|
+
},
|
3115
|
+
],
|
3116
|
+
},
|
3117
|
+
{
|
3118
|
+
path: ':app/comments/m/:module/e/:id',
|
3119
|
+
component: AXMCommentListViewComponent,
|
3120
|
+
},
|
3121
|
+
];
|
3122
|
+
return routes;
|
3123
|
+
}
|
2821
3124
|
class AXMConversationModule {
|
2822
3125
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
2823
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule,
|
2824
|
-
// Chat Components
|
2825
|
-
AXMChatComponent,
|
2826
|
-
AXMChatItemComponent,
|
2827
|
-
AXMChatPreviewComponent,
|
2828
|
-
// Comment Components
|
2829
|
-
AXMCommentListViewComponent], imports: [
|
2830
|
-
// Common Modules
|
2831
|
-
CommonModule,
|
2832
|
-
FormsModule, i2$1.RouterModule,
|
2833
|
-
// Acorex Core Modules
|
2834
|
-
AXFormatModule,
|
2835
|
-
AXConversationModule,
|
2836
|
-
AXCommentModule,
|
2837
|
-
AXTranslationModule,
|
2838
|
-
// Acorex Component Modules
|
2839
|
-
AXResizableDirective,
|
2840
|
-
AXImageModule,
|
2841
|
-
AXAvatarModule,
|
2842
|
-
AXBadgeModule,
|
2843
|
-
AXDecoratorModule,
|
2844
|
-
AXTextBoxModule,
|
2845
|
-
AXSearchBoxModule,
|
2846
|
-
AXTabsModule,
|
2847
|
-
AXButtonModule,
|
2848
|
-
AXLoadingModule,
|
2849
|
-
AXWysiwygModule,
|
2850
|
-
AXLabelModule,
|
2851
|
-
AXSelectBoxModule,
|
2852
|
-
AXFormModule,
|
2853
|
-
AXDropdownButtonModule,
|
2854
|
-
AXDropdownModule,
|
2855
|
-
AXToolBarModule,
|
2856
|
-
AXSkeletonModule,
|
2857
|
-
AXPPageLayoutComponent,
|
2858
|
-
AXPThemeLayoutBlockComponent,
|
2859
|
-
AXPThemeLayoutStartSideComponent,
|
2860
|
-
AXPThemeLayoutHeaderComponent,
|
2861
|
-
AXPThemeLayoutToolbarComponent], exports: [
|
2862
|
-
// Chat Components
|
2863
|
-
AXMChatItemComponent,
|
2864
|
-
AXMChatPreviewComponent,
|
2865
|
-
// Comment Components
|
2866
|
-
AXMCommentListViewComponent,
|
3126
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, exports: [
|
2867
3127
|
// Modules
|
2868
3128
|
RouterModule] }); }
|
2869
3129
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, providers: [
|
@@ -2891,6 +3151,11 @@ class AXMConversationModule {
|
|
2891
3151
|
useClass: AXMConversationModuleEntityProvider,
|
2892
3152
|
multi: true,
|
2893
3153
|
},
|
3154
|
+
{
|
3155
|
+
provide: ROUTES,
|
3156
|
+
multi: true,
|
3157
|
+
useFactory: routesFactory,
|
3158
|
+
},
|
2894
3159
|
// Menu Provider
|
2895
3160
|
// {
|
2896
3161
|
// provide: AXP_MENU_PROVIDER,
|
@@ -2899,94 +3164,17 @@ class AXMConversationModule {
|
|
2899
3164
|
// },
|
2900
3165
|
// Conversation Module
|
2901
3166
|
importProvidersFrom(AXConversationModule.forRoot()),
|
3167
|
+
DatePipe,
|
2902
3168
|
], imports: [
|
2903
|
-
// Common Modules
|
2904
|
-
CommonModule,
|
2905
|
-
FormsModule,
|
2906
|
-
RouterModule.forChild(routes),
|
2907
|
-
// Acorex Core Modules
|
2908
|
-
AXFormatModule,
|
2909
|
-
AXConversationModule,
|
2910
|
-
AXCommentModule,
|
2911
|
-
AXTranslationModule,
|
2912
|
-
AXImageModule,
|
2913
|
-
AXAvatarModule,
|
2914
|
-
AXBadgeModule,
|
2915
|
-
AXDecoratorModule,
|
2916
|
-
AXTextBoxModule,
|
2917
|
-
AXSearchBoxModule,
|
2918
|
-
AXTabsModule,
|
2919
|
-
AXButtonModule,
|
2920
|
-
AXLoadingModule,
|
2921
|
-
AXWysiwygModule,
|
2922
|
-
AXLabelModule,
|
2923
|
-
AXSelectBoxModule,
|
2924
|
-
AXFormModule,
|
2925
|
-
AXDropdownButtonModule,
|
2926
|
-
AXDropdownModule,
|
2927
|
-
AXToolBarModule,
|
2928
|
-
AXSkeletonModule,
|
2929
|
-
AXPPageLayoutComponent,
|
2930
|
-
AXPThemeLayoutBlockComponent,
|
2931
|
-
AXPThemeLayoutStartSideComponent,
|
2932
|
-
AXPThemeLayoutHeaderComponent,
|
2933
|
-
AXPThemeLayoutToolbarComponent,
|
2934
3169
|
// Modules
|
2935
3170
|
RouterModule] }); }
|
2936
3171
|
}
|
2937
3172
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: AXMConversationModule, decorators: [{
|
2938
3173
|
type: NgModule,
|
2939
3174
|
args: [{
|
2940
|
-
declarations: [
|
2941
|
-
|
2942
|
-
AXMChatComponent,
|
2943
|
-
AXMChatItemComponent,
|
2944
|
-
AXMChatPreviewComponent,
|
2945
|
-
// Comment Components
|
2946
|
-
AXMCommentListViewComponent,
|
2947
|
-
// Layout Components
|
2948
|
-
],
|
2949
|
-
imports: [
|
2950
|
-
// Common Modules
|
2951
|
-
CommonModule,
|
2952
|
-
FormsModule,
|
2953
|
-
RouterModule.forChild(routes),
|
2954
|
-
// Acorex Core Modules
|
2955
|
-
AXFormatModule,
|
2956
|
-
AXConversationModule,
|
2957
|
-
AXCommentModule,
|
2958
|
-
AXTranslationModule,
|
2959
|
-
// Acorex Component Modules
|
2960
|
-
AXResizableDirective,
|
2961
|
-
AXImageModule,
|
2962
|
-
AXAvatarModule,
|
2963
|
-
AXBadgeModule,
|
2964
|
-
AXDecoratorModule,
|
2965
|
-
AXTextBoxModule,
|
2966
|
-
AXSearchBoxModule,
|
2967
|
-
AXTabsModule,
|
2968
|
-
AXButtonModule,
|
2969
|
-
AXLoadingModule,
|
2970
|
-
AXWysiwygModule,
|
2971
|
-
AXLabelModule,
|
2972
|
-
AXSelectBoxModule,
|
2973
|
-
AXFormModule,
|
2974
|
-
AXDropdownButtonModule,
|
2975
|
-
AXDropdownModule,
|
2976
|
-
AXToolBarModule,
|
2977
|
-
AXSkeletonModule,
|
2978
|
-
AXPPageLayoutComponent,
|
2979
|
-
AXPThemeLayoutBlockComponent,
|
2980
|
-
AXPThemeLayoutStartSideComponent,
|
2981
|
-
AXPThemeLayoutHeaderComponent,
|
2982
|
-
AXPThemeLayoutToolbarComponent,
|
2983
|
-
],
|
3175
|
+
declarations: [],
|
3176
|
+
imports: [],
|
2984
3177
|
exports: [
|
2985
|
-
// Chat Components
|
2986
|
-
AXMChatItemComponent,
|
2987
|
-
AXMChatPreviewComponent,
|
2988
|
-
// Comment Components
|
2989
|
-
AXMCommentListViewComponent,
|
2990
3178
|
// Modules
|
2991
3179
|
RouterModule,
|
2992
3180
|
],
|
@@ -3015,6 +3203,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
|
|
3015
3203
|
useClass: AXMConversationModuleEntityProvider,
|
3016
3204
|
multi: true,
|
3017
3205
|
},
|
3206
|
+
{
|
3207
|
+
provide: ROUTES,
|
3208
|
+
multi: true,
|
3209
|
+
useFactory: routesFactory,
|
3210
|
+
},
|
3018
3211
|
// Menu Provider
|
3019
3212
|
// {
|
3020
3213
|
// provide: AXP_MENU_PROVIDER,
|
@@ -3023,6 +3216,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
|
|
3023
3216
|
// },
|
3024
3217
|
// Conversation Module
|
3025
3218
|
importProvidersFrom(AXConversationModule.forRoot()),
|
3219
|
+
DatePipe,
|
3026
3220
|
],
|
3027
3221
|
}]
|
3028
3222
|
}] });
|