1/**
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import prompt from '@system.prompt';
16
17import HiLog from '../../utils/HiLog';
18import common from '../../data/commonData';
19import ContactsService from '../../service/ContactsService';
20import commonService from '../../service/CommonService'
21import commonCtrl from '../../pages/conversation/common'
22import LooseObject from '../../data/LooseObject'
23
24const TAG = 'ReceiveController';
25
26export default class ReceiveController {
27    private static sInstance: ReceiveController;
28    commonCtrl = commonCtrl.getInstance();
29    refresh: boolean = false;
30    // Recipient information (selected)
31    selectContacts: Array<any> = [];
32    contacts: Array<any> = [];
33    // Recipient list information (all)
34    contactsTemp: Array<any> = [];
35    // Recipient Content
36    myText: string = '';
37    // true: focus editing status (gray); false: no focus (blue)
38    isInputStatus: boolean = true;
39    // true Show Search List
40    isShowSearch: boolean = true;
41    strSelectContact: string = '';
42    styleTextarea: string = 'select-contact-textarea';
43    hasBlur: boolean = false;
44    // List pagination, number of pages
45    page: number = 0;
46    // List pagination, quantity
47    limit: number = 10;
48    // Total number of contacts
49    totalMessage: number = 0;
50
51    static getInstance() {
52        if (ReceiveController.sInstance == null) {
53            ReceiveController.sInstance = new ReceiveController();
54        }
55        return ReceiveController.sInstance;
56    }
57
58    onInit(call) {
59        HiLog.i(TAG, 'onInit()');
60        this.selectContacts = this.commonCtrl.paramContact.transmitContacts;
61        if (this.selectContacts.length > 0) {
62            let that = this;
63            setTimeout(function () {
64                that.setContactValue(call);
65            }, 200);
66            this.isShowSearch = false;
67            this.setInputStatus(false);
68        }
69        this.requestItem();
70        this.refresh = !this.refresh;
71    }
72
73    onBackPress() {
74        this.InputTextClear();
75        return true;
76    }
77
78    InputTextClear() {
79        this.myText = '';
80        this.setInputStatus(true);
81        this.commonCtrl.paramContact.transmitContacts = [];
82        HiLog.i(TAG,'InputTextClear');
83    }
84
85    requestItem() {
86        let count = this.page * this.limit;
87        if (this.page === 0) {
88            this.page++;
89            this.queryContacts();
90        } else if (count < this.totalMessage && this.contacts.length > (this.page - 1) * this.limit) {
91            // The restriction on Contacts is to prevent multiple refresh requests during initialization.
92            this.page++;
93            this.queryContacts();
94        }
95    }
96
97    queryContacts() {
98        let actionData = {
99            page: this.page,
100            limit: this.limit
101        };
102        // Querying Contacts
103        ContactsService.getInstance().queryContact(actionData, contacts => {
104            if (common.int.SUCCESS == contacts.code) {
105                let response = this.contacts.concat(contacts.response);
106                this.contacts = [];
107                this.contacts = response;
108                this.contactsTemp = this.contacts.slice(0);
109            } else {
110                HiLog.w(TAG, 'queryContacts, fail');
111            }
112        }, null);
113        // Number of statistics
114        ContactsService.getInstance().queryContactSizeByCondition(actionData, res => {
115            if (res.code == common.int.SUCCESS) {
116                this.totalMessage = res.abilityResult;
117            }
118        }, null);
119    }
120
121    searchContacts(textValue, callback) {
122        HiLog.i(TAG, 'searchContacts start');
123        let actionData = {
124            telephone: textValue,
125        };
126        ContactsService.getInstance().queryContactViewByCondition(actionData, res => {
127            if (res.code == common.int.SUCCESS) {
128                this.contacts = res.abilityResult;
129            } else {
130                HiLog.w(TAG, 'searchContacts fail');
131            }
132            callback(res.code);
133        }, null);
134    }
135
136    // Filter search terms to match contacts
137    filterContacts(textValue) {
138        try {
139            this.contacts = this.contactsTemp.filter((contact) => {
140                if (contact.contactName && contact.contactName.toLowerCase().search(textValue) != -1) {
141                    HiLog.i(TAG, 'filterContacts, contactName');
142                    return true;
143                } else if (contact.telephone && contact.telephone.toLowerCase().search(textValue) != -1) {
144                    HiLog.i(TAG, 'filterContacts, telephone');
145                    return true;
146                }
147                return false;
148            });
149        } catch (Error) {
150            HiLog.i(TAG, `error message: ${JSON.stringify(Error)}`);
151        }
152    }
153
154    isPhoneNumber(str) {
155        // Determine whether the value is a number.
156        let reg = /^\d{1,}$/;
157        let pattern = new RegExp(reg);
158        return pattern.test(str);
159    }
160
161    setInputStatus(flag) {
162        this.isInputStatus = flag;
163        if (!flag) {
164            this.strSelectContact = this.setShowContactName();
165        }
166    }
167
168    checkReceive(call) {
169        HiLog.i(TAG, 'checkReceive, isInputStatus: ' + this.isInputStatus);
170        if (this.myText.trim() == common.string.EMPTY_STR) {
171            this.strSelectContact = this.setShowContactName();
172            this.isShowSearch = false;
173            return;
174        }
175        this.hasBlur = true;
176        if (this.isPhoneNumber(this.myText)) {
177            // Get information from the contact list
178            let that = this;
179            let selectContact: LooseObject = {};
180            let hasSelect = false;
181            for (let index in this.contacts) {
182                let contact = this.contacts[index];
183                if (contact.telephone == that.myText) {
184                    selectContact.headImage = 'icon/user_avatar_full_fill.svg';
185                    selectContact.contactName = contact.contactName;
186                    selectContact.telephone = contact.telephone;
187                    selectContact.telephoneFormat = contact.telephone;
188                    selectContact.select = false;
189                    hasSelect = true;
190                    break;
191                }
192            }
193            if (!hasSelect) {
194                selectContact.headImage = common.string.EMPTY_STR;
195                selectContact.contactName = common.string.EMPTY_STR;
196                selectContact.telephone = that.myText;
197                selectContact.telephoneFormat = that.myText;
198                selectContact.select = false;
199            }
200            HiLog.i(TAG, 'checkReceive, isPhoneNumber yes');
201            this.selectContacts.push(selectContact);
202            this.refresh = !this.refresh;
203            this.setInputStatus(false);
204            this.isShowSearch = false;
205            this.setContactValue(call);
206        } else {
207            HiLog.i(TAG, 'checkReceive, isPhoneNumber no');
208            prompt.showToast({
209                // Invalid Recipient
210                // @ts-ignore
211                message: $r('app.string.invalid_receive', this.myText),
212                duration: 1000,
213            });
214            this.myText = '';
215            this.isShowSearch = true;
216            this.setContactValue(call);
217        }
218    }
219
220    searchChange(text, call) {
221        HiLog.d(TAG, 'searchChange, start');
222        if (this.checkSingle()) {
223            this.setInputStatus(false);
224            this.isShowSearch = false;
225            return;
226        }
227        this.myText = text;
228        if (!this.isInputStatus) {
229            HiLog.w(TAG, 'searchChange, isInputStatus false');
230            return;
231        }
232        this.searchContacts(this.myText, code => {
233            if (code == common.int.SUCCESS && this.myText.trim() != '') {
234                this.setContactValue(call);
235                this.dealSearchData();
236                this.setContactValue(call);
237            } else {
238                this.setContactValue(call);
239            }
240        });
241    }
242
243    dealSearchData() {
244        if (this.myText.trim() == '') {
245            this.contacts = this.contactsTemp.slice(0);
246        } else {
247            let textValue = this.myText.trim().toLowerCase();
248            // Filtering logic
249            this.filterContacts(textValue);
250        }
251    }
252
253    setContactValue(call) {
254        // Send recipient information to the invoked parent component.
255        call({
256            // Select the content of the text box before the contact.
257            contactValue: this.myText,
258            // Selected recipient information
259            selectContacts: this.selectContacts,
260            // Whether the focus is lost
261            hasBlur: this.hasBlur
262        });
263    }
264
265    addContact(index, call) {
266        let curItem = this.contacts[index];
267        if (this.checkSingle()) {
268            return;
269        }
270        if (curItem == undefined) {
271            HiLog.e(TAG, 'addContact contact is null');
272            return;
273        }
274        if (curItem != undefined && curItem.telephone != undefined && curItem.telephone.toString().trim() == '') {
275            prompt.showToast({
276                // Invalid Recipient
277                // @ts-ignore
278                message: $r('app.string.invalid_receive', this.myText),
279                duration: 1000,
280            });
281            return;
282        }
283        this.selectContacts.push(curItem);
284        this.contactsTemp = this.contactsTemp.filter((item) => {
285            if (item.telephone == undefined || curItem.telephone == undefined) {
286                return;
287            }
288            HiLog.w(TAG, 'addContact');
289            return item.telephone != curItem.telephone
290        });
291        this.contacts.splice(index, 1);
292        HiLog.i(TAG, 'addContact, length: ' + this.selectContacts.length);
293        this.myText = '';
294        if (this.selectContacts.length == 1) {
295            this.setInputStatus(false);
296            this.isShowSearch = false;
297            this.setContactValue(call);
298        } else {
299            this.setInputStatus(true);
300            this.isShowSearch = true;
301            this.setContactValue(call);
302        }
303        HiLog.i(TAG, 'addContact, isInputStatus: ' + this.isInputStatus);
304    }
305
306    setShowContactName() {
307        if (this.selectContacts.length == 0) {
308            return '';
309        }
310        let myName = this.selectContacts[0]?.contactName?.trim();
311        if (!myName) {
312            myName = ''
313        }
314        let telephone = this.selectContacts[0]?.telephone
315        if (telephone === undefined || telephone === null) {
316            HiLog.e(TAG, 'setShowContactName fail');
317            return '';
318        }
319        if (!this.isPhoneNumber(telephone)) {
320            myName = telephone.replace(new RegExp(/e|-|#|\*|\./, 'g'), common.string.EMPTY_STR);
321        } else {
322            if (myName == '') {
323                myName = telephone;
324            }
325        }
326        if (this.selectContacts.length >= 2) {
327            // name and other numbers
328            return $r('app.string.and_others', myName, this.selectContacts.length - 1)
329        } else {
330            return myName
331        }
332    }
333
334    myContactFocus() {
335        HiLog.i(TAG, 'myContactFocus, start');
336        this.myText = common.string.EMPTY_STR;
337        this.setInputStatus(true);
338        this.isShowSearch = true;
339    }
340
341    myContactClick() {
342        HiLog.i(TAG, 'myContactClick, start');
343        if (!this.isInputStatus) {
344            this.myText = common.string.EMPTY_STR;
345            this.setInputStatus(true);
346            this.isShowSearch = true;
347            // The TextArea control does not support focus.
348            //      this.$element('receiveTxt').focus({
349            //        focus: true
350            //      });
351        }
352    }
353
354    nameClick(idx, call) {
355        HiLog.i(TAG, 'click-->' + idx);
356        if (this.selectContacts[idx] == null || this.selectContacts[idx] == undefined) {
357            return;
358        }
359        if (this.selectContacts[idx].select && this.selectContacts[idx].select != undefined) {
360            let item = this.selectContacts.splice(idx, 1);
361            // Deleted items are added to the collection to be searched.
362            this.contactsTemp.push(item);
363            if (item[0] != undefined && item[0].telephoneFormat != undefined &&
364            item[0].telephoneFormat.toString().trim() != '') {
365                this.contacts.push(item[0]);
366            }
367            this.refresh = !this.refresh;
368            this.setContactValue(call);
369            return;
370        }
371        for (let element of this.selectContacts) {
372            element.select = false;
373        }
374        this.selectContacts[idx].select = true;
375        this.refresh = !this.refresh;
376    }
377
378    clickToContacts(call) {
379        var actionData: LooseObject = {};
380        actionData.pageFlag = common.contactPage.PAGE_FLAG_SINGLE_CHOOSE;
381        this.jumpToContactForResult(actionData, call);
382    }
383    // Tap a contact's avatar to go to the contact details page.
384    titleBarAvatar(index) {
385        var actionData: LooseObject = {};
386        actionData.phoneNumber = this.contacts[index]?.telephone;
387        actionData.pageFlag = common.contactPage.PAGE_FLAG_CONTACT_DETAILS;
388        this.jumpToContact(actionData);
389    }
390    // Switching to the Contacts app
391    jumpToContact(actionData) {
392        let str = commonService.commonContactParam(actionData);
393        globalThis.mmsContext.startAbility(str).then((data) => {
394        }).catch((error) => {
395            HiLog.i(TAG, 'jumpToContact failed');
396        });
397    }
398    // Switching to the Contacts app
399    async jumpToContactForResult(actionData, call) {
400        let str = commonService.commonContactParam(actionData);
401        var data = await globalThis.mmsContext.startAbilityForResult(str);
402        if (data.resultCode == 0) {
403            this.dealContactParams(data.want.parameters.contactObjects, call);
404        }
405    }
406
407    dealContactParams(contactObjects, call) {
408        this.selectContacts = [];
409        let params = JSON.parse(contactObjects);
410        if (this.checkSingle()) {
411            return;
412        }
413        for (let element of params) {
414            let selectContact: LooseObject = {};
415            selectContact.headImage = 'icon/user_avatar_full_fill.svg';
416            selectContact.contactName = element.contactName;
417            selectContact.telephone = element.telephone;
418            selectContact.telephoneFormat = element.telephone;
419            selectContact.select = false;
420            this.selectContacts.push(selectContact);
421        }
422        if (params.length > 1 && this.checkSingle()) {
423            this.selectContacts = [];
424            return;
425        }
426        if (this.selectContacts.length > 0) {
427            this.deleteRepetitionContacts(this.contacts, this.selectContacts);
428            this.setInputStatus(false);
429            this.isShowSearch = false;
430            this.setContactValue(call);
431        }
432        this.commonCtrl.paramContact.isSelectContact = false;
433        this.commonCtrl.paramContact.isNewRecallMessagesFlag = false;
434    }
435
436    deleteRepetitionContacts(contacts, selectContacts) {
437        let indexs = [];
438        let count = 0;
439        for (let item of contacts) {
440            let telephone = item.telephone;
441            for (let selectContact of selectContacts) {
442                if (telephone == selectContact.telephone) {
443                    indexs.push(count);
444                    break;
445                }
446            }
447            count++;
448        }
449        let selectContactIndexs = [];
450        for (let i = 0; i < selectContacts.length; i++) {
451            let telephone = selectContacts[i].telephone;
452            for (let j = i + 1; j < selectContacts.length; j++) {
453                if (telephone == selectContacts[j].telephone) {
454                    selectContactIndexs.push(i);
455                    break;
456                }
457            }
458        }
459        if (indexs.length > 0) {
460            for (let index of indexs) {
461                contacts.splice(index, 1);
462            }
463        }
464        if (selectContactIndexs.length > 0) {
465            for (let index of selectContactIndexs) {
466                selectContacts.splice(index, 1);
467            }
468        }
469    }
470
471    // Currently, only one recipient is supported. You can enter only one recipient first.
472    checkSingle() {
473        if (this.selectContacts.length > 0) {
474            prompt.showToast({
475                // Invalid Recipient
476                // @ts-ignore
477                message: '只支持单个收件人',
478                duration: 1000,
479            });
480            return true;
481        } else {
482            return false;
483        }
484    }
485}