· 6 years ago · Apr 29, 2019, 09:26 AM
1class SyncUserContactsAction implements AsyncAction {
2 @override
3 ThunkAction<AppState> execute(api) => (store) {
4 print('SyncContactsAction.execute()');
5
6 final session = store.state.authState.loginState.value;
7 if (session == null) return;
8
9 final currentState = () => store.state.domainState.syncUserContactsState;
10 if (currentState().isInProgress()) return;
11
12 store.dispatch(SyncUserContactsStateAction(AsyncState.inProgress(currentState().value)));
13
14 Observable.fromFuture(_checkPermission())
15 .concatMap((_) => Observable.fromFuture(_getPhoneContacts(store)))
16 .concatMap((result) => Observable.fromFuture(api.domain.syncContacts(session, result)))
17 .flatMap((map) {
18 if (map.isEmpty) return Observable.just(currentState().value);
19 return Observable.fromIterable(map)
20 .bufferCount(100)
21 .concatMap((result) => (_loadUserContacts(store, result)).asStream())
22 .flatMap((res) => _handleSyncResponse(store, res).asStream())
23 .last
24 .asObservable()
25 .delay(Duration(milliseconds: 200));
26 }).listen(
27 (results) {
28 store.dispatch(SyncUserContactsStateAction(AsyncState.success(results)));
29 },
30 onError: (error) =>
31 store.dispatch(SyncUserContactsStateAction(AsyncState.failed(error, currentState().value))),
32 );
33 };
34
35 Async.Future _checkPermission() async {
36 if (defaultTargetPlatform == TargetPlatform.android) {
37 var permission = await permissionHandler.checkPermissionStatus(PermissionGroup.contacts);
38 if (permission != PermissionStatus.granted) {
39 final permissions = await permissionHandler.requestPermissions([PermissionGroup.contacts]);
40 permission = permissions.values.first;
41 }
42 if (permission != PermissionStatus.granted)
43 throw AsyncError(AsyncErrorType.PERMISSION_DENIED, AsyncErrorSeverity.ERROR);
44 }
45 }
46
47 Async.Future<List<UserContact>> _handleSyncResponse(
48 Store<AppState> store, List<UserContact> results) async {
49 final session = store.state.authState.loginState.value;
50 if (session == null) return [];
51
52 final db = await getDbConnection(session.user.id);
53
54 final newValue = await db.transaction<List<UserContact>>((transaction) async {
55 final newContacts = List<UserContact>.from(store.state.domainState.syncUserContactsState.value ?? []);
56 results.forEach((result) async {
57 newContacts.add(result);
58 await insertContact(transaction, result);
59 });
60
61 return newContacts;
62 });
63
64 store.dispatch(SyncUserContactsStateAction(AsyncState.inProgress(newValue)));
65
66 return newValue;
67 }
68
69 Async.Future<List<Contact>> _getPhoneContacts(Store<AppState> store) async {
70 // contact service doesn't request the permission automatically
71
72 final session = store.state.authState.loginState.value;
73 if (session == null) return [];
74
75 var phoneContacts;
76 try {
77 phoneContacts = (await cs.ContactsService.getContacts())
78 .map((c) => Contact.fromPhoneContact(c))
79 .where((c) => c.name != null && c.phones.isNotEmpty)
80 .toList();
81 } catch (e) {
82 print('error: $e');
83 }
84
85 List<UserContact> userContacts;
86 String path = join(await getDatabasesPath(), "${session.user.id}.db");
87 Database dbOpen = await openDatabase(path,
88 version: 1,
89 onCreate: (db, _) async =>
90 await db.execute('CREATE TABLE IF NOT EXISTS contacts_json(id VARCHAR(50), json TEXT)'));
91
92 var contactsJson = await dbOpen.query('contacts_json');
93 userContacts = contactsJson
94 .map((data) => UserContact.fromJson(UserContactSource.LOCAL, jsonDecode(data['json'])))
95 .toList();
96
97 final db = await getDbConnection(session.user.id);
98
99 await db.transaction<List<UserContact>>((transaction) async {
100 Observable.fromFuture(phoneContacts)
101 .concatMap((phoneContact) {
102 final existing = userContacts.where((el) =>
103 el.phones
104 .map((a) => a.value)
105 .where((b) => phoneContact.phones.map((d) => d.value).contains(b))
106 .length >
107 0);
108 // create?
109 if (existing.length == 0) {
110 final time = new DateTime.now().millisecondsSinceEpoch;
111 Observable.just(new UserContact(UserContactSource.LOCAL, time, null, phoneContact))
112 .doOnData((data) {
113 userContacts.add(data);
114 return data;
115 })
116 .doOnData((data) => insertContact(transaction, data))
117 .listen(null);
118 }
119 })
120 .listen(null);
121 });
122
123 store.dispatch(SyncUserContactsStateAction(AsyncState.inProgress(userContacts)));
124
125 return phoneContacts;
126 }
127
128 Async.Future<List<UserContact>> _loadUserContacts(
129 Store<AppState> store, List<UserContact> userContacts) async {
130 final session = store.state.authState.loginState.value;
131
132 if (session == null) return [];
133
134 for (UserContact c in userContacts) {
135 try {
136 final contacts = (await cs.ContactsService.getContacts(query: c.name)).where((el) =>
137 el.phones.map((a) => a.value).where((b) => c.phones.map((d) => d.value).contains(b)).length > 0);
138 if (contacts.length == 0) {
139 await cs.ContactsService.addContact(new cs.Contact(
140 givenName: c.name,
141 phones: c.phones.map((i) => new cs.Item(label: '', value: i.value)),
142 emails: c.emails.map((i) => new cs.Item(label: '', value: i.value)),
143 ));
144 }
145 } catch (e) {
146 print('Failed to create contact. ${e}');
147 }
148 }
149
150 return userContacts;
151 }
152}