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 */
15
16import router from '@ohos.router';
17import { HiLog } from '../../../../../../common/src/main/ets/util/HiLog';
18import { ContactVo } from '../../model/bean/ContactVo';
19import { ArrayUtil } from '../../../../../../common/src/main/ets/util/ArrayUtil';
20import { CallLogRepository } from '../../../../../../feature/call/src/main/ets/repo/CallLogRepository';
21import { ContactRepository } from '../../../../../../feature/contact/src/main/ets/repo/ContactRepository';
22import emitter from '@ohos.events.emitter';
23import ContactListDataSource from '../../model/bean/ContactListDataSource';
24import WorkerWrapper from '../../workers/base/WorkerWrapper';
25import { SearchContactsBean } from '../../model/bean/SearchContactsBean';
26import SearchContactsSource from '../../model/bean/SearchContactsSource';
27import AlphabetIndexerPresenter from './alphabetindex/AlphabetIndexerPresenter';
28
29const TAG = 'ContactListPresenter  ';
30const DELAY_TIME: number = 1000;
31const EMITTER_SEARCH_ID: number = 105;
32/**
33 * Type of the control that is clicked in the contact list.
34 */
35export enum ContactClickType {
36  LOGIN_IN,
37  IMPORT_CONTACT,
38  CREATE_CONTACT,
39  SCAN_CARD
40}
41
42/**
43 * Contact List Logical Interface Model
44 */
45export default class ContactListPresenter {
46  private static sInstance: ContactListPresenter;
47  indexs: string[] = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '…'];
48  contactListPages: Array<number> = [];
49  curItem: ContactVo = new ContactVo('', '', '', '', '', '', true, '', '');
50  page: number = 0;
51  limit: number = 0;
52  refreshIndex: number = 0;
53  queryContactsType: string = 'all';
54  isEmptyGroup: boolean = true;
55  shareList: Resource[] = [$r('app.string.qr_code'), $r('app.string.v_card'), $r('app.string.text')];
56  settingsMenu: Resource[] = [$r('app.string.contact_setting_type_scancard'), $r('app.string.call_setting_type_setting')];
57  contactListDataSource: ContactListDataSource = new ContactListDataSource();
58  searchContactsSource: SearchContactsSource = new SearchContactsSource();
59  isShow: boolean = false;
60  context: Context;
61  worker: WorkerWrapper;
62  loading: boolean;
63  initStarted: boolean = false;
64  contactSearchList: SearchContactsBean[] = [];
65  contactSearchCount: number = 0;
66  tempValue: string = '';
67  taskId: number = -1;;
68  isSearchBackgroundColor: boolean = true;
69  isSearchPage: boolean = false;
70  inputKeyword: string = '';
71  contactList: ContactVo[] = [];
72  alphabetIndexPresenter: AlphabetIndexerPresenter = AlphabetIndexerPresenter.getInstance();
73
74  refreshState: () => void
75  onContactChange = () => {
76    HiLog.i(TAG, 'onContactChange refresh');
77    this.setDelayTask();
78  }
79
80  private constructor() {
81  }
82
83  public static getInstance(): ContactListPresenter {
84    if (globalThis.presenterManager?.contactListPresenter) {
85      return globalThis.presenterManager.contactListPresenter;
86    }
87    if (ContactListPresenter.sInstance == null) {
88      HiLog.i(TAG, 'Contact getInstance!');
89      ContactListPresenter.sInstance = new ContactListPresenter();
90    }
91    return ContactListPresenter.sInstance;
92  }
93
94  onCreate(context: Context, worker: WorkerWrapper) {
95    this.context = context;
96    this.worker = worker;
97    ContactRepository.getInstance().registerDataChangeObserver(this.onContactChange);
98  }
99
100  onDestroy() {
101    ContactRepository.getInstance().unRegisterDataChangeObserver(this.onContactChange);
102  }
103
104  bindUI(refreshState?: () => void) {
105    this.refreshState = refreshState;
106  }
107
108  startInit() {
109    if (!this.initStarted) {
110      this.initStarted = true;
111      HiLog.i(TAG, 'startInit taskId:' + this.taskId + ',page:' + this.page);
112      if (this.page == 0 || this.taskId !== -1) {
113        this.setDelayTask();
114      } else {
115        this.refreshContinue();
116      }
117    } else if (this.taskId !== -1) {
118      HiLog.i(TAG, 'startInit page has init taskId:' + this.taskId + ',page:' + this.page);
119      this.setDelayTask();
120    }
121  }
122
123  cachePageOne() {
124    if (!this.initStarted) {
125      HiLog.i(TAG, 'cachePageOne')
126      if (!this.loading) {
127        this.page = 0;
128        this.refreshIndex = 0;
129      }
130      this.setDelayTask();
131    } else {
132      HiLog.i(TAG, 'no need cachePageOne');
133    }
134  }
135
136  aboutToAppear() {
137    HiLog.i(TAG, 'Contact aboutToAppear!');
138    this.startInit();
139  }
140
141  aboutToDisappear() {
142    HiLog.i(TAG, 'Contact aboutToDisappear!');
143  }
144
145  setPageShow(isShow: boolean) {
146    if (this.isShow == isShow) {
147      return;
148    }
149    this.isShow = isShow;
150    if (this.isShow) {
151      this.startInit();
152    }
153    this.contactListDataSource.setIsShow(isShow);
154  }
155
156  requestItem() {
157    HiLog.i(TAG, 'Contacts requestItem!');
158    if (this.page == 0) {
159      this.page++;
160      this.refreshContinue();
161    } else if (!this.initStarted) {
162      this.cachePageOne();
163    } else {
164      HiLog.i(TAG, 'isLoading');
165    }
166  }
167
168  setDelayTask() {
169    if (this.taskId !== -1) {
170      clearTimeout(this.taskId);
171      this.taskId = -1;
172    }
173    let delay: number = 0;
174    if (this.page != 0) {
175      delay += DELAY_TIME;
176    }
177    if (!this.isShow) {
178      delay += DELAY_TIME;
179    }
180    if (delay > 0) {
181      this.taskId = setTimeout(() => {
182        if (this.page != 0) {
183          this.setDelayTask();
184        } else {
185          this.requestItem();
186        }
187      }, delay);
188    } else {
189      this.requestItem();
190    }
191  }
192
193  refreshContinue() {
194    if (this.page == 0) {
195      HiLog.i(TAG, 'refreshContinue end');
196      return;
197    }
198    if (this.page == 1) {
199      this.limit = 50;
200    } else {
201      this.limit = 500;
202    }
203    this.refreshPage(this.page, this.limit, () => {
204      if (this.initStarted) {
205        setTimeout(() => {
206          this.refreshContinue();
207        },!this.isShow ? 700 : 1);
208      }
209    })
210  }
211
212  refreshPage(page: number, limit: number, callback?: () => void) {
213    this.loading = true;
214    let actionData: any = {};
215    actionData.page = page;
216    actionData.limit = limit;
217    this.worker.sendRequest('getAllContact', {
218      actionData: actionData,
219      context: this.context
220    }, (result) => {
221      HiLog.i(TAG, `refreshPage ${page} getAllContact, length is: ` + result.length);
222      if (Array.prototype.isPrototypeOf(result)) {
223        this.contactListDataSource.refresh(this.refreshIndex, this.contactListPages[page -1], result);
224        this.contactList.splice(this.refreshIndex, this.contactListPages[page -1], ...result);
225        this.alphabetIndexPresenter.initContactList(this.contactList);
226      }
227      this.contactListPages[page -1] = result.length;
228      this.refreshIndex += this.contactListPages[page -1];
229      let contactCount = result.length;
230      if (contactCount < limit) {
231        HiLog.i(TAG, 'Contacts load completed: ' + this.contactListDataSource.totalCount());
232        if (this.contactListPages.length > page) {
233          this.contactListPages.splice(page, this.contactListPages.length - page);
234        }
235        if (this.contactListDataSource.totalCount() > this.refreshIndex) {
236          this.contactListDataSource.remove(this.refreshIndex, this.contactListDataSource.totalCount() - this.refreshIndex);
237        }
238        if (this.refreshState) {
239          this.refreshState();
240        }
241        this.page = 0;
242        this.refreshIndex = 0;
243        this.loading = false;
244      } else {
245        if (this.refreshState) {
246          this.refreshState();
247        }
248        if (this.taskId === -1) {
249          this.page = page + 1;
250          if (callback) {
251            callback();
252          }
253        } else {
254          // If there's a waiting mission, there's no need to continue.
255          this.page = 0;
256          this.refreshIndex = 0;
257          this.setDelayTask()
258        }
259        this.loading = false;
260      }
261    })
262  }
263
264
265  /**
266   * Touch and hold the current contact information saved by a contact item in the contact list.
267   * The information can be deleted or shared later.
268   *
269   * @param curItem
270   */
271  setCurItem(curItem: ContactVo) {
272    HiLog.i(TAG, 'setCurItem');
273    this.curItem = curItem;
274  }
275
276  /**
277   * Cancel button for deleting a dialog box.
278   */
279  onDeleteDialogCancel() {
280    HiLog.i(TAG, 'onDeleteDialogCancel !!!');
281  }
282
283  /**
284   * Confirm button for deleting a dialog box
285   *
286   * @param result
287   */
288  onDeleteDialogConfirm(index, item) {
289    HiLog.i(TAG, 'onDeleteDialogConfirm !!! ');
290    this.worker.sendRequest('deleteContactById', {
291      context: this.context,
292      contactId: item.contactId
293    }, (result) => {
294      if (result) {
295        HiLog.w(TAG, 'deleteContactById error:' + JSON.stringify(result))
296      }
297    });
298    this.contactListDataSource.remove(index);
299  }
300
301  /**
302   * Share Cancel Button
303   */
304  onShareDialogCancel() {
305    HiLog.i(TAG, 'onShareDialogCancel !!! ');
306  }
307
308  /**
309   * Share confirmation button
310   */
311  onShareDialogConfirm() {
312    HiLog.i(TAG, 'onShareDialogConfirm !!! ');
313  }
314
315  /**
316   * Event callback when an item is clicked in the sharing dialog box
317   *
318   * @param item item
319   * @param index index
320   */
321  onShareItemClick(item: any, index: number | null) {
322    HiLog.i(TAG, 'onShareItemClick !!! index is %s' + index);
323  }
324
325  /**
326   * Log in to Huawei ID.
327   */
328  loginAccount() {
329    HiLog.i(TAG, 'loginAccount !!');
330    router.push(
331      {
332        url: '',
333        params: {}
334      }
335    );
336  }
337
338  /**
339   * Import Contact dialog box
340   */
341  importContact() {
342    HiLog.i(TAG, 'importContact !!');
343    router.push(
344      {
345        url: '',
346        params: {}
347      }
348    );
349  }
350
351  /**
352   * New Contact
353   */
354  createContact() {
355    HiLog.i(TAG, 'createContact !!');
356    router.push(
357      {
358        url: 'pages/contacts/accountants/Accountants'
359      }
360    );
361  }
362
363  /**
364   * Scan the business card.
365   */
366  scanCard() {
367    HiLog.i(TAG, 'scanCard !!');
368    router.push(
369      {
370        url: ''
371      }
372    );
373  }
374
375  getSearchContact(value: string) {
376    HiLog.i(TAG, 'getSearchContact start.');
377    if ('' === value) {
378      this.isSearchBackgroundColor = true;
379      this.searchContactsSource.refresh(this.contactSearchList);
380      let innerEvent = {
381        eventId: 102,
382        priority: emitter.EventPriority.HIGH
383      };
384      emitter.emit(innerEvent, {
385        data: {
386          'contactSearchList': 0
387        }
388      });
389      return;
390    }
391    this.tempValue = value;
392    let actionData: any = {};
393    actionData.value = this.tempValue;
394    globalThis.DataWorker.sendRequest('getSearchContact', {
395      actionData: actionData,
396      context: globalThis.context
397    }, (result) => {
398      this.isSearchBackgroundColor = false;
399      this.contactSearchList = result;
400      this.contactSearchCount = this.contactSearchList.length;
401      this.searchContactsSource.refresh(this.contactSearchList);
402      let innerEvent = {
403        eventId: 102,
404        priority: emitter.EventPriority.HIGH
405      };
406      emitter.emit(innerEvent, {
407        data: {
408          'contactSearchList': this.contactSearchCount
409        }
410      });
411    })
412    HiLog.i(TAG, 'getSearchContact end.');
413  }
414
415  sendEmitter(isSearchPage: boolean) {
416    let innerEvent = {
417      eventId: EMITTER_SEARCH_ID,
418      priority: emitter.EventPriority.HIGH
419    };
420    emitter.emit(innerEvent, {
421      data: {
422        'isSearchPage': isSearchPage
423      }
424    });
425  }
426}