@chat21/chat21-ionic 3.0.76-rc.1 → 3.0.76-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/package.json +1 -1
  3. package/src/app/chatlib/conversation-detail/conversation-content/conversation-content.component.html +1 -1
  4. package/src/app/chatlib/conversation-detail/conversation-content/conversation-content.component.ts +15 -1
  5. package/src/app/chatlib/conversation-detail/ion-conversation-detail/ion-conversation-detail.component.html +1 -1
  6. package/src/app/chatlib/conversation-detail/ion-conversation-detail/ion-conversation-detail.component.scss +9 -2
  7. package/src/app/chatlib/conversation-detail/message/bubble-message/bubble-message.component.html +8 -1
  8. package/src/app/chatlib/conversation-detail/message/bubble-message/bubble-message.component.scss +5 -3
  9. package/src/app/components/canned-response/canned-response.component.html +26 -0
  10. package/src/app/components/canned-response/canned-response.component.scss +141 -0
  11. package/src/app/components/canned-response/canned-response.component.spec.ts +24 -0
  12. package/src/app/components/canned-response/canned-response.component.ts +281 -0
  13. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +9 -8
  14. package/src/app/pages/conversation-detail/conversation-detail.module.ts +2 -0
  15. package/src/app/pages/conversation-detail/conversation-detail.page.html +25 -12
  16. package/src/app/pages/conversation-detail/conversation-detail.page.scss +14 -29
  17. package/src/app/pages/conversation-detail/conversation-detail.page.ts +80 -357
  18. package/src/global.scss +0 -137
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # chat21-ionic ver 3.0
2
2
 
3
+ ### 3.0.76-rc.2
4
+ - added: canned component to manage canned responses
5
+ - bug-fixed: if more than oe '/' is present in message-text-area and a canned is selected, replace canned text on the last '/' character in the message string
6
+ - bug-fixed: close canned component if user click again on canned-icon-button
7
+
3
8
  ### 3.0.76-rc.1
4
9
  - changed: senderFullName in list conversation for guest users with guest#uuid[0..5]
5
10
  - added: multi-language to MEMBER_LEFT_GROUP and LEAD_UPDATED info messages
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-ionic",
3
3
  "author": "Tiledesk SRL",
4
- "version": "3.0.76-rc.1",
4
+ "version": "3.0.76-rc.2",
5
5
  "license": "MIT License",
6
6
  "homepage": "https://tiledesk.com/",
7
7
  "repository": {
@@ -1,4 +1,4 @@
1
- <div class="c21-body" (click)="hideMenuOption()">
1
+ <div class="c21-body" (click)="hideOutsideElements()">
2
2
 
3
3
  <div class="c21-body-container">
4
4
 
@@ -256,7 +256,21 @@ export class ConversationContentComponent implements OnInit {
256
256
  return false;
257
257
  }
258
258
 
259
- hideMenuOption() {
259
+ isSameSender(senderId, index):boolean{
260
+ if(senderId && this.messages[index - 1] && (senderId === this.messages[index - 1].sender)){
261
+ return true;
262
+ }
263
+ return false;
264
+ }
265
+
266
+ isFirstMessage(senderId, index):boolean{
267
+ if(senderId && index == 0 && this.messages[index] && (this.messages[index] !== senderId)){
268
+ return true;
269
+ }
270
+ return false;
271
+ }
272
+
273
+ hideOutsideElements() {
260
274
  this.onMenuOptionShow.emit(false)
261
275
  }
262
276
 
@@ -102,7 +102,7 @@
102
102
  </div>
103
103
 
104
104
  <!-- message RECIPIENT:: -->
105
- <div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message) && isChannelTypeGroup(channelType)"
105
+ <div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message) && isChannelTypeGroup(channelType) && !isSameSender(message?.sender, i)"
106
106
  class="message_sender_fullname">
107
107
  {{message.sender_fullname}}
108
108
  </div>
@@ -245,15 +245,22 @@ ion-item {
245
245
  // left: -26px;
246
246
  }
247
247
 
248
- :host .base_receive .msg_receive ::ng-deep div > div > ion-button {
248
+ :host .base_receive .msg_receive ::ng-deep div > div > div > ion-button.canned {
249
249
  display: none;
250
250
  position: absolute;
251
251
  top: -3px;
252
252
  right: -31px;
253
253
  }
254
254
 
255
+ :host .base_receive .msg_receive ::ng-deep div > div > div >ion-button.emoji {
256
+ display: none;
257
+ position: absolute;
258
+ top: -3px;
259
+ right: -51px;
260
+ }
261
+
255
262
  // :host .base_receive .msg_receive:hover ::ng-deep div > div > ion-button {
256
- :host .base_receive:hover .msg_receive ::ng-deep div > div > ion-button {
263
+ :host .base_receive:hover .msg_receive ::ng-deep div > div > div > ion-button {
257
264
  display: block;
258
265
  // position: absolute;
259
266
  // top: -11px;
@@ -61,12 +61,19 @@
61
61
  </chat-html>
62
62
 
63
63
  <ng-container *ngIf="areVisibleCAR && supportMode">
64
- <ion-button shape="round" size="small" class="btn-add-msg-as-canned-response" ion-button fill="clear"
64
+ <ion-button shape="round" size="small" class="btn-add-msg canned" ion-button fill="clear"
65
65
  (click)="presentCreateCannedResponseModal()" tooltip="{{addAsCannedResponseTooltipText}}"
66
66
  [options]="tooltipOptions" placement="bottom">
67
67
  <ion-icon slot="icon-only" name="flash-outline" style="font-size: 1em;"> </ion-icon>
68
68
  </ion-button>
69
69
  </ng-container>
70
+ <!-- <ng-container *ngIf="supportMode">
71
+ <ion-button shape="round" size="small" class="btn-add-msg emoji" ion-button fill="clear"
72
+ (click)="presentEmojiiModal()" tooltip="{{addAsCannedResponseTooltipText}}"
73
+ [options]="tooltipOptions" placement="bottom">
74
+ <ion-icon slot="icon-only" name="happy-outline" style="font-size: 1em;"> </ion-icon>
75
+ </ion-button>
76
+ </ng-container> -->
70
77
  </div>
71
78
  </div>
72
79
 
@@ -48,13 +48,15 @@
48
48
 
49
49
  }
50
50
  // > .button-native
51
- .btn-add-msg-as-canned-response {
52
- // padding-left: 5px ;
53
- // padding-right: 5px ;
51
+ .btn-add-msg {
54
52
  border-radius: 50%;
55
53
  --padding-end: 7px;
56
54
  --padding-start: 7px;
57
55
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.6);
56
+
57
+ ion-icon{
58
+ font-size: 1.2em;
59
+ }
58
60
  }
59
61
 
60
62
 
@@ -0,0 +1,26 @@
1
+ <div id="canned">
2
+ <ion-list class="canned-list" *ngIf="tagsCannedFilter.length > 0">
3
+ <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
4
+ class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
5
+ *ngFor="let canned of tagsCannedFilter; let i = index;"
6
+ (click)="onClickCannedFN(canned, $event)">
7
+ <div class="cannedContent">
8
+ <ion-input [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" [disabled]="canned.disabled"></ion-input>
9
+ <ion-input [(ngModel)]="canned.text" *ngIf="canned.text" class="text" [disabled]="canned.disabled"></ion-input>
10
+ </div>
11
+ <ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && !canned.disabled" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
12
+ <ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && canned.disabled" (click)="onEditCanned(canned, $event)"></ion-icon>
13
+ <ion-icon class="canned-item-icon" name="trash-bin-outline" slot=end *ngIf="canned.createdBy === loggedUser.uid" (click)="onDeleteCanned(canned, $event)"></ion-icon>
14
+ </ion-item>
15
+ <ion-item class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
16
+ <ion-icon class="add-canned-response-icon" name="flash-outline"></ion-icon>
17
+ <span class="add-canned-response-add-icon">+</span>
18
+ <label class="add-canned-response-label" >{{translationMap?.get('AddNewCannedResponse')}}</label>
19
+ </ion-item>
20
+ </ion-list>
21
+ <!-- <ion-list class="canned-list" *ngIf="tagsCannedFilter.length === 0 && HIDE_CANNED_RESPONSES === false">
22
+ <ion-item button="true" >
23
+ There are no canned responses available
24
+ </ion-item>
25
+ </ion-list> -->
26
+ </div>
@@ -0,0 +1,141 @@
1
+ .canned-item.add-canned-response-wpr:hover > span {
2
+ color: #1877f2;
3
+ }
4
+
5
+ .canned-item.add-canned-response-wpr:hover > ion-icon {
6
+ color: #1877f2;
7
+ }
8
+
9
+ .canned-item.add-canned-response-wpr:hover > label {
10
+ color: #1877f2;
11
+ }
12
+
13
+ .is_active_item {
14
+ // background-color: #eaf1fd!important;
15
+ --background: #eaf1fd !important;
16
+ }
17
+
18
+
19
+
20
+ .header {
21
+ display: flex;
22
+ flex-direction: row;
23
+ -webkit-box-pack: start;
24
+ justify-content: flex-end;
25
+ -webkit-box-align: center;
26
+ align-items: center;
27
+ border-bottom: 1px solid rgb(239, 242, 246);
28
+ padding: 0px 10px;
29
+ background: white;
30
+ width: 100%;
31
+ box-shadow: rgb(0, 27, 71, .24) 0px 8px 20px;
32
+ .canned-response-icon-header{
33
+ color: #3880ff !important;
34
+ font-size: 1.4em;
35
+ margin: 8px 5px;
36
+ }
37
+
38
+ }
39
+
40
+ .add-canned-response-icon {
41
+ color: #b3bfd0;
42
+ cursor: pointer;
43
+ font-size: 18px;
44
+ }
45
+ .add-canned-response-label {
46
+ color: #b3bfd0;
47
+ cursor: pointer;
48
+ }
49
+ .add-canned-response-add-icon {
50
+ color: #b3bfd0;
51
+ cursor: pointer;
52
+ position: relative;
53
+ top: 4px;
54
+ left: -6px;
55
+ }
56
+
57
+ .canned-list {
58
+ // position: absolute;
59
+ // bottom: 0;
60
+ background-color: white !important;
61
+ width: 100%;
62
+ max-height: 310px;
63
+ overflow-y: auto;
64
+ // padding: 10px 0;
65
+ // margin: 0;
66
+ margin-bottom: 1px;
67
+ font-size: 14px;
68
+ line-height: 1.42857143;
69
+ color: #080f1a;
70
+ box-sizing: border-box;
71
+ -webkit-font-smoothing: antialiased;
72
+ // list-style: none;
73
+ z-index: 999999;
74
+
75
+ .cannedContent{
76
+ width: 100%;
77
+ }
78
+
79
+ ion-input {
80
+ --padding-bottom: 0px;
81
+ --padding-top: 0px;
82
+ &.text{
83
+ font-style: italic;
84
+ }
85
+ &.title {
86
+ font-weight: 500;
87
+ }
88
+ }
89
+ .native-input[disabled] {
90
+ opacity: 10 !important;
91
+ }
92
+ ion-icon {
93
+ zoom: 0.7;
94
+ }
95
+ .no-ripple {
96
+ --ripple-color: transparent;
97
+ --background-activated: transparent;
98
+ }
99
+
100
+ .border{
101
+ border-bottom: 1px dashed rgb(211, 219, 229) !important;
102
+ margin: 0px 4px
103
+ }
104
+
105
+
106
+ }
107
+
108
+ .canned-item {
109
+ -webkit-tap-highlight-color: transparent;
110
+ font-family: Lato, sans-serif;
111
+ font-size: 14px;
112
+ line-height: 1.42857143;
113
+ list-style: none;
114
+ box-sizing: border-box;
115
+ -webkit-font-smoothing: antialiased;
116
+ // margin: 0 10px;
117
+ position: relative;
118
+ outline: none;
119
+ color: #434a54;
120
+ // padding: 10px;
121
+ padding: 5px;
122
+ width: auto;
123
+ cursor: pointer;
124
+ // .item-inner{
125
+ // border: none!important;
126
+ // }
127
+ }
128
+
129
+ ion-item {
130
+ --background-hover: #1877f2 !important;
131
+
132
+ .nocannedTitle {
133
+ color: #f44336;
134
+ }
135
+ .no-canned-available-text {
136
+ color: #1877f2 !important;
137
+ }
138
+ .no-canned-available-text:hover {
139
+ text-decoration: underline;
140
+ }
141
+ }
@@ -0,0 +1,24 @@
1
+ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { IonicModule } from '@ionic/angular';
3
+
4
+ import { CannedResponseComponent } from './canned-response.component';
5
+
6
+ describe('CannedResponseComponent', () => {
7
+ let component: CannedResponseComponent;
8
+ let fixture: ComponentFixture<CannedResponseComponent>;
9
+
10
+ beforeEach(async(() => {
11
+ TestBed.configureTestingModule({
12
+ declarations: [ CannedResponseComponent ],
13
+ imports: [IonicModule.forRoot()]
14
+ }).compileComponents();
15
+
16
+ fixture = TestBed.createComponent(CannedResponseComponent);
17
+ component = fixture.componentInstance;
18
+ fixture.detectChanges();
19
+ }));
20
+
21
+ it('should create', () => {
22
+ expect(component).toBeTruthy();
23
+ });
24
+ });
@@ -0,0 +1,281 @@
1
+ import { Component, Input, OnInit, SimpleChange, ElementRef, Output, EventEmitter, HostListener } from '@angular/core';
2
+ import { CreateCannedResponsePage } from 'src/app/pages/create-canned-response/create-canned-response.page';
3
+ import { CannedResponsesService } from 'src/app/services/canned-responses/canned-responses.service';
4
+ import { TiledeskService } from 'src/app/services/tiledesk/tiledesk.service';
5
+ import { UserModel } from 'src/chat21-core/models/user';
6
+ import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
7
+ import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
8
+ import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk-auth.service';
9
+ import { compareValues, htmlEntities } from 'src/chat21-core/utils/utils';
10
+
11
+ @Component({
12
+ selector: 'app-canned-response',
13
+ templateUrl: './canned-response.component.html',
14
+ styleUrls: ['./canned-response.component.scss'],
15
+ })
16
+ export class CannedResponseComponent implements OnInit {
17
+
18
+ // @Input() tagsCannedFilter: any = []
19
+ @Input() conversationWith: string;
20
+ @Input() conversationWithFullname: string;
21
+ @Input() currentString: string;
22
+ @Input() translationMap: Map<string, string>;
23
+ @Output() onLoadedCannedResponses = new EventEmitter<[any]>();
24
+ @Output() onClickCanned = new EventEmitter<any>();
25
+ @Output() onClickAddCannedResponse = new EventEmitter();
26
+ public loggedUser: UserModel
27
+
28
+ public tagsCanned: any = []
29
+ public tagsCannedCount: number
30
+ public tagsCannedFilter: any = []
31
+
32
+ public arrowkeyLocation = -1
33
+
34
+ private logger: LoggerService = LoggerInstance.getInstance();
35
+ constructor(
36
+ public tiledeskAuthService: TiledeskAuthService,
37
+ public tiledeskService: TiledeskService,
38
+ public cannedResponsesService: CannedResponsesService,
39
+ public el: ElementRef
40
+ ) { }
41
+
42
+ ngOnInit() {
43
+ this.loggedUser = this.tiledeskAuthService.getCurrentUser()
44
+ }
45
+
46
+ ngOnChanges(changes: SimpleChange){
47
+ this.logger.debug('[CANNED] - loadTagsCanned strSearch ', this.currentString)
48
+ if(this.currentString !== undefined){
49
+ this.loadTagsCanned(this.currentString, this.conversationWith)
50
+ }
51
+ }
52
+
53
+ // ----------------------------------------------------------
54
+ // @ CANNED RESPONSES methods
55
+ // ----------------------------------------------------------
56
+ loadTagsCanned(strSearch, conversationWith) {
57
+ this.logger.log('[CANNED] - loadTagsCanned strSearch ', strSearch)
58
+ this.logger.log('[CANNED] - loadTagsCanned conversationWith ', conversationWith)
59
+
60
+ const conversationWith_segments = conversationWith.split('-')
61
+ // Removes the last element of the array if is = to the separator
62
+ if (conversationWith_segments[conversationWith_segments.length - 1] === '') {
63
+ conversationWith_segments.pop()
64
+ }
65
+
66
+ this.logger.log('[CANNED] - loadTagsCanned conversationWith_segments ', conversationWith_segments)
67
+ let projectId = ''
68
+
69
+ if (conversationWith_segments.length === 4) {
70
+ projectId = conversationWith_segments[2]
71
+ this.logger.log('[CANNED] - loadTagsCanned projectId ', projectId)
72
+ this.getAndShowCannedResponses(strSearch, projectId)
73
+ } else {
74
+ this.getProjectIdByConversationWith(strSearch, this.conversationWith)
75
+ }
76
+ }
77
+
78
+ getProjectIdByConversationWith(strSearch, conversationWith: string) {
79
+ const tiledeskToken = this.tiledeskAuthService.getTiledeskToken()
80
+
81
+ this.tiledeskService.getProjectIdByConvRecipient(tiledeskToken, conversationWith).subscribe((res) => {
82
+ this.logger.log('[CANNED] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT RES', res)
83
+ if (res) {
84
+ const projectId = res.id_project
85
+ this.logger.log('[CANNED] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT projectId ', projectId)
86
+ if (projectId) {
87
+ this.getAndShowCannedResponses(strSearch, projectId)
88
+ }
89
+ }
90
+ }, (error) => {
91
+ this.logger.error('[CANNED] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT - ERROR ', error)
92
+ }, () => {
93
+ this.logger.log('[CANNED] - loadTagsCanned - GET PROJECTID BY CONV RECIPIENT * COMPLETE *')
94
+ })
95
+ }
96
+
97
+ getAndShowCannedResponses(strSearch, projectId) {
98
+ const tiledeskToken = this.tiledeskAuthService.getTiledeskToken()
99
+ this.logger.log('[CANNED] - loadTagsCanned tagsCanned.length', this.tagsCanned.length)
100
+ //if(this.tagsCanned.length <= 0 ){
101
+ this.tagsCanned = []
102
+ this.cannedResponsesService.getAll(tiledeskToken, projectId).subscribe((res) => {
103
+ this.logger.log('[CANNED] - loadTagsCanned getCannedResponses RES', res)
104
+
105
+ this.tagsCanned = res
106
+ this.tagsCannedCount = res.length
107
+ this.logger.log('[CANNED] - loadTagsCanned getCannedResponses tagsCannedCount', this.tagsCannedCount)
108
+ // if (this.HIDE_CANNED_RESPONSES === false) {
109
+ this.showTagsCanned(strSearch)
110
+ // }
111
+ }, (error) => {
112
+ this.logger.error('[CANNED] - loadTagsCanned getCannedResponses - ERROR ', error)
113
+ }, () => {
114
+ this.logger.log('[CANNED] - loadTagsCanned getCannedResponses * COMPLETE *')
115
+ })
116
+ }
117
+
118
+ showTagsCanned(strSearch) {
119
+ this.logger.log('[CANNED] - showTagsCanned strSearch ', strSearch)
120
+ this.tagsCannedFilter = []
121
+ var tagsCannedClone = JSON.parse(JSON.stringify(this.tagsCanned))
122
+ this.logger.log('[CANNED] - showTagsCanned tagsCannedClone ', tagsCannedClone)
123
+ //this.logger.log("that.contacts lenght:: ", strSearch);
124
+ this.tagsCannedFilter = this.filterItems(tagsCannedClone, strSearch)
125
+ this.logger.log('[CANNED] - showTagsCanned tagsCannedFilter ', this.tagsCannedFilter)
126
+
127
+ this.tagsCannedFilter.sort(compareValues('title', 'asc'))
128
+ var strReplace = strSearch
129
+ if (strSearch.length > 0) {
130
+ strReplace = "<b class='highlight-search-string'>" + strSearch + '</b>'
131
+ }
132
+ // for (var i = 0; i < this.tagsCannedFilter.length; i++) {
133
+ // let text = htmlEntities(this.tagsCannedFilter[i].text);
134
+ // // const textCanned = "<div class='cannedText'>" + this.replacePlaceholderInCanned(this.tagsCannedFilter[i].text) + '</div>'
135
+ // const textCanned = "<div class='cannedText'>" + this.replacePlaceholderInCanned(text) + '</div>'
136
+ // let title = htmlEntities(this.tagsCannedFilter[i].title)
137
+ // // this.tagsCannedFilter[i].title = "<div class='cannedContent'><div class='cannedTitle'>" + this.tagsCannedFilter[i].title.toString().replace(strSearch, strReplace.trim()) + '</div>' + textCanned + '</div>'
138
+ // this.tagsCannedFilter[i].title = "<div class='cannedContent'><div class='cannedTitle'>" + title.toString().replace(strSearch, strReplace.trim()) + '</div>' + textCanned + '</div>'
139
+ // }
140
+ this.tagsCannedFilter.forEach(canned => {
141
+ canned.text = this.replacePlaceholderInCanned(canned.text);
142
+ canned.disabled = true
143
+ });
144
+ if (this.tagsCannedCount === 0) {
145
+ // const button = this.renderer.createElement('button');
146
+ // const buttonText = this.renderer.createText('Click me');
147
+ // this.renderer.appendChild(button, buttonText);
148
+ // console.log('[CANNED] - this.el.nativeElement ', this.el.nativeElement)
149
+ // this.renderer.listen(button, 'click', () => { alert('hi'); });
150
+ // let nocanned = {}
151
+ // if (this.USER_ROLE !== 'agent') {
152
+ const nocanned = {
153
+ // "<div class='cannedContent'><div class='cannedTitle nocannedTitle #noCannedTitle'>" + this.translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE') + ".</div><div class='cannedText'>" + this.translationMap.get('TO_CREATE_THEM_GO_TO_THE_PROJECT') + '</div></div>'
154
+ // <div class='cannedText no-canned-available-text'>" + this.translationMap.get('AddNewCannedResponse') + '</div>
155
+ title: this.translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE') ,
156
+ text: '',
157
+ }
158
+ // } else if (this.USER_ROLE === 'agent') {
159
+ // nocanned = {
160
+ // // "<div class='cannedContent'><div class='cannedTitle nocannedTitle #noCannedTitle'>" + this.translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE') + ".</div><div class='cannedText'>" + this.translationMap.get('TO_CREATE_THEM_GO_TO_THE_PROJECT') + '</div></div>'
161
+ // title:
162
+ // "<div class='cannedContent'><div class='cannedTitle nocannedTitle #noCannedTitle'>" + this.translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE') + ".</div></div>",
163
+ // text: 'There are no canned responses available',
164
+ // }
165
+ // }
166
+ this.tagsCannedFilter.push(nocanned)
167
+ }
168
+ this.onLoadedCannedResponses.emit(this.tagsCannedFilter)
169
+ }
170
+
171
+ filterItems(items, searchTerm) {
172
+ this.logger.log('[CANNED] filterItems tagsCannedClone ', items, ' searchTerm: ', searchTerm)
173
+ //this.logger.log("filterItems::: ",searchTerm);
174
+ return items.filter((item) => {
175
+ //this.logger.log("filterItems::: ", item.title.toString().toLowerCase());
176
+ this.logger.log('[CANNED] filtered tagsCannedClone item ', item)
177
+ return item.title.toString().toLowerCase().indexOf(searchTerm.toString().toLowerCase()) > -1
178
+ })
179
+ }
180
+
181
+ replacePlaceholderInCanned(str) {
182
+ this.logger.log('[CANNED] - replacePlaceholderInCanned str ', str)
183
+ str = str.replace('$recipient_name', this.conversationWithFullname)
184
+ if (this.loggedUser && this.loggedUser.fullname) {
185
+ str = str.replace('$agent_name', this.loggedUser.fullname)
186
+ }
187
+ return str
188
+ }
189
+
190
+
191
+ onEditCanned(canned, ev){
192
+ ev.preventDefault()
193
+ ev.stopPropagation()
194
+ canned.disabled = false
195
+ this.logger.log('[CANNED] onEditCanned ', canned)
196
+ setTimeout(() => {
197
+ this.el.nativeElement.querySelector('#titleCanned_'+canned._id).setFocus()
198
+ }, 500);
199
+ }
200
+
201
+ onConfirmEditCanned(canned, ev){
202
+ ev.preventDefault()
203
+ ev.stopPropagation()
204
+ const tiledeskToken = this.tiledeskAuthService.getTiledeskToken()
205
+ this.logger.log('[CANNED] onConfirmEditCanned ', canned, ev)
206
+ this.cannedResponsesService.edit(tiledeskToken, canned.id_project, canned).subscribe(cannedRes=> {
207
+ canned.disabled = true
208
+ }, (error) => {
209
+ this.logger.error('[CANNED] - onConfirmEditCanned - ERROR ', error)
210
+ }, () => {
211
+ this.logger.log('[CANNED] - onConfirmEditCanned * COMPLETE *')
212
+ })
213
+ }
214
+
215
+ onDeleteCanned(canned, ev){
216
+ ev.preventDefault()
217
+ ev.stopPropagation()
218
+ const tiledeskToken = this.tiledeskAuthService.getTiledeskToken()
219
+ this.logger.log('[CANNED] onDeleteCanned ', canned)
220
+ this.cannedResponsesService.delete(tiledeskToken, canned.id_project, canned._id).subscribe(cannedRes=> {
221
+ if(cannedRes.status === 1000){
222
+ this.tagsCannedFilter.splice(this.tagsCannedFilter.findIndex(el => el._id === canned._id), 1)
223
+ }
224
+ }, (error) => {
225
+ this.logger.error('[CANNED] - onConfirmEditCanned - ERROR ', error)
226
+ }, () => {
227
+ this.logger.log('[CANNED] - onConfirmEditCanned * COMPLETE *')
228
+ })
229
+ }
230
+
231
+ onClickCannedFN(canned, event){
232
+ if(!canned.disabled){
233
+ event.preventDefault();
234
+ event.stopPropagation();
235
+ } else if(this.tagsCannedCount > 0){
236
+ this.onClickCanned.emit(canned)
237
+ } else {
238
+ this.logger.log('[CANNED] THERE IS NOT CANNED ', canned.text)
239
+ }
240
+ }
241
+
242
+ onClickAddCannedResponseFN(){
243
+ this.onClickAddCannedResponse.emit()
244
+ }
245
+
246
+
247
+ @HostListener('document:keydown', ['$event'])
248
+ handleKeyboardEvent(event: KeyboardEvent) {
249
+ this.logger.log("CONVERSATION-DETAIL handleKeyboardEvent event.key ", event);
250
+
251
+ if (this.tagsCannedFilter.length > 0) {
252
+ if (event.key === 'ArrowDown') {
253
+ this.arrowkeyLocation++
254
+ if (this.arrowkeyLocation === this.tagsCannedFilter.length) {
255
+ this.arrowkeyLocation--
256
+ }
257
+ // this.replaceTagInMessage(this.tagsCannedFilter[this.arrowkeyLocation])
258
+ } else if (event.key === 'ArrowUp') {
259
+ if (this.arrowkeyLocation > 0) {
260
+ this.arrowkeyLocation--
261
+ } else if (this.arrowkeyLocation < 0) {
262
+ this.arrowkeyLocation++
263
+ }
264
+ // this.replaceTagInMessage(this.tagsCannedFilter[this.arrowkeyLocation])
265
+ }
266
+
267
+ if (event.key === 'Enter') {
268
+ const canned_selected = this.tagsCannedFilter[this.arrowkeyLocation]
269
+ this.logger.log('[CONVS-DETAIL] replaceTagInMessage canned_selected ',canned_selected)
270
+ if (canned_selected) {
271
+ this.arrowkeyLocation = -1
272
+ this.tagsCannedFilter = []
273
+ this.onClickCanned.emit(canned_selected)
274
+ // event.preventDefault();
275
+ // return false;
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ }
@@ -1,5 +1,5 @@
1
1
  import { UserModel } from 'src/chat21-core/models/user';
2
- import { Component, OnInit, Output, EventEmitter, Input, AfterViewInit, ViewChild, ElementRef, OnChanges, HostListener, Renderer2 } from '@angular/core';
2
+ import { Component, OnInit, Output, EventEmitter, Input, AfterViewInit, ViewChild, ElementRef, OnChanges, HostListener, Renderer2, SimpleChange, SimpleChanges } from '@angular/core';
3
3
 
4
4
  import { Chooser } from '@ionic-native/chooser/ngx';
5
5
  import { IonTextarea, ModalController, ToastController } from '@ionic/angular';
@@ -43,7 +43,7 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
43
43
 
44
44
  @Input() loggedUser: UserModel;
45
45
  @Input() conversationWith: string;
46
- @Input() tagsCannedFilter: any = [];
46
+ @Input() tagsCannedFilter: any;
47
47
  @Input() tagsCannedCount: number;
48
48
  @Input() areVisibleCAR: boolean;
49
49
  @Input() supportMode: boolean;
@@ -54,8 +54,8 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
54
54
  @Input() dropEvent: any;
55
55
  @Input() disableTextarea: boolean;
56
56
  @Output() eventChangeTextArea = new EventEmitter<object>();
57
- @Output() hasClickedOpenCannedResponses = new EventEmitter<boolean>();
58
57
  @Output() eventSendMessage = new EventEmitter<object>();
58
+ @Output() onClickOpenCannedResponses = new EventEmitter<boolean>();
59
59
  @Output() onPresentModalScrollToBottom = new EventEmitter<boolean>();
60
60
 
61
61
  public conversationEnabled = false;
@@ -69,6 +69,7 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
69
69
  public currentWindowWidth: any;
70
70
  private logger: LoggerService = LoggerInstance.getInstance();
71
71
  public countClicks: number = 0;
72
+ public openCanned: boolean = false;
72
73
  public IS_SUPPORT_GROUP_CONVERSATION: boolean;
73
74
  public IS_ON_MOBILE_DEVICE: boolean;
74
75
  TYPE_MSG_TEXT = TYPE_MSG_TEXT;
@@ -140,9 +141,7 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
140
141
  }
141
142
 
142
143
 
143
-
144
-
145
- ngOnChanges() {
144
+ ngOnChanges(changes: SimpleChanges) {
146
145
  if (this.translationMap) {
147
146
  // this.LONG_TEXAREA_PLACEHOLDER = this.translationMap.get('LABEL_ENTER_MSG')
148
147
  // this.SHORT_TEXAREA_PLACEHOLDER = this.translationMap.get('LABEL_ENTER_MSG_SHORT')
@@ -580,7 +579,8 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
580
579
  }
581
580
 
582
581
  openCannedResponses() {
583
- this.hasClickedOpenCannedResponses.emit(true);
582
+ this.openCanned = !this.openCanned
583
+ this.onClickOpenCannedResponses.emit(this.openCanned);
584
584
  }
585
585
 
586
586
 
@@ -698,8 +698,9 @@ export class MessageTextAreaComponent implements OnInit, AfterViewInit, OnChange
698
698
 
699
699
  @HostListener('document:keydown', ['$event'])
700
700
  handleKeyboardEvent(event: KeyboardEvent) {
701
-
701
+ this.logger.log("[CONVS-DETAIL][MSG-TEXT-AREA] handleKeyboardEvent event.key ", event);
702
702
  // Note: on mac keyboard "metakey" matches "cmd"
703
+
703
704
  if (event.key === 'Enter' && event.altKey || event.key === 'Enter' && event.ctrlKey || event.key === 'Enter' && event.metaKey) {
704
705
  this.logger.log('[CONVS-DETAIL][MSG-TEXT-AREA] HAS PRESSED COMBO KEYS this.messageString', this.messageString);
705
706
  if (this.messageString !== undefined && this.messageString.trim() !== '') {