Skip to content

Commit 492cb49

Browse files
committed
feat: add listener.isListening
1 parent 6cfea82 commit 492cb49

File tree

4 files changed

+52
-18
lines changed

4 files changed

+52
-18
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ console.log(`Contact was ${updated ? 'updated' : 'not updated'}.`)
234234

235235
This module exposes an `EventEmitter`, which can be used to listen to potential changes to the `CNContactStore`. When a contact is changed either with methods contained in this module, or manually by a user, the `contact-changed` event will be emitted.
236236

237-
Owing to the underlying architecture of this module, the listener must be manually managed; before use you must initialize it with `listener.setup()` and when you are finished listening for events you must remove it with `listener.remove()`
237+
Owing to the underlying architecture of this module, the listener must be manually managed; before use you must initialize it with `listener.setup()` and when you are finished listening for events you must remove it with `listener.remove()`. To check if a listener is currently active, use `listener.isListening()`.
238238

239239
Example Usage:
240240

contacts.mm

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -171,36 +171,29 @@
171171
contact.Set("contactThumbnailImage", image_buffer);
172172
}
173173

174-
if ([cncontact isKeyAvailable:CNContactJobTitleKey]) {
174+
if ([cncontact isKeyAvailable:CNContactJobTitleKey])
175175
contact.Set("jobTitle", std::string([[cncontact jobTitle] UTF8String]));
176-
}
177176

178-
if ([cncontact isKeyAvailable:CNContactDepartmentNameKey]) {
177+
if ([cncontact isKeyAvailable:CNContactDepartmentNameKey])
179178
contact.Set("departmentName",
180179
std::string([[cncontact departmentName] UTF8String]));
181-
}
182180

183-
if ([cncontact isKeyAvailable:CNContactOrganizationNameKey]) {
181+
if ([cncontact isKeyAvailable:CNContactOrganizationNameKey])
184182
contact.Set("organizationName",
185183
std::string([[cncontact organizationName] UTF8String]));
186-
}
187184

188-
if ([cncontact isKeyAvailable:CNContactNoteKey]) {
185+
if ([cncontact isKeyAvailable:CNContactNoteKey])
189186
contact.Set("note", std::string([[cncontact note] UTF8String]));
190-
}
191187

192-
if ([cncontact isKeyAvailable:CNContactMiddleNameKey]) {
188+
if ([cncontact isKeyAvailable:CNContactMiddleNameKey])
193189
contact.Set("middleName", std::string([[cncontact middleName] UTF8String]));
194-
}
195190

196-
if ([cncontact isKeyAvailable:CNContactInstantMessageAddressesKey]) {
191+
if ([cncontact isKeyAvailable:CNContactInstantMessageAddressesKey])
197192
contact.Set("instantMessageAddresses",
198193
GetInstantMessageAddresses(env, cncontact));
199-
}
200194

201-
if ([cncontact isKeyAvailable:CNContactSocialProfilesKey]) {
195+
if ([cncontact isKeyAvailable:CNContactSocialProfilesKey])
202196
contact.Set("socialProfiles", GetSocialProfiles(env, cncontact));
203-
}
204197

205198
return contact;
206199
}
@@ -509,8 +502,16 @@ CNAuthorizationStatus AuthStatus() {
509502
return Napi::Boolean::New(env, success);
510503
}
511504

505+
// Sets up event listening for changes to the CNContactStore.
512506
Napi::Boolean SetupListener(const Napi::CallbackInfo &info) {
513507
Napi::Env env = info.Env();
508+
509+
if (observer) {
510+
Napi::Error::New(env, "An observer is already observing")
511+
.ThrowAsJavaScriptException();
512+
return Napi::Boolean::New(env, false);
513+
}
514+
514515
ts_fn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(),
515516
"emitCallback", 0, 1);
516517

@@ -529,27 +530,46 @@ CNAuthorizationStatus AuthStatus() {
529530
return Napi::Boolean::New(env, true);
530531
}
531532

533+
// Removes event listening for changes to the CNContactStore.
532534
Napi::Boolean RemoveListener(const Napi::CallbackInfo &info) {
533535
Napi::Env env = info.Env();
534536

535537
if (!observer) {
536-
Napi::Error::New(env, "No observers are currently listening")
538+
Napi::Error::New(env, "No observers are currently observing")
537539
.ThrowAsJavaScriptException();
538540
return Napi::Boolean::New(env, false);
539541
}
540542

543+
// Release thread-safe function.
541544
ts_fn.Release();
545+
546+
// Remove observer from the Notification Center.
542547
[[NSNotificationCenter defaultCenter] removeObserver:observer];
543548

549+
// Reset observer.
550+
observer = nullptr;
551+
544552
return Napi::Boolean::New(env, true);
545553
}
546554

555+
// Indicates whether event listening for changes to the CNContactStore is in
556+
// place.
557+
Napi::Boolean IsListening(const Napi::CallbackInfo &info) {
558+
Napi::Env env = info.Env();
559+
560+
bool is_listening = observer != nullptr;
561+
562+
return Napi::Boolean::New(env, is_listening);
563+
}
564+
547565
// Initializes all functions exposed to JS.
548566
Napi::Object Init(Napi::Env env, Napi::Object exports) {
549567
exports.Set(Napi::String::New(env, "setupListener"),
550568
Napi::Function::New(env, SetupListener));
551569
exports.Set(Napi::String::New(env, "removeListener"),
552570
Napi::Function::New(env, RemoveListener));
571+
exports.Set(Napi::String::New(env, "isListening"),
572+
Napi::Function::New(env, IsListening));
553573
exports.Set(Napi::String::New(env, "getAuthStatus"),
554574
Napi::Function::New(env, GetAuthStatus));
555575
exports.Set(Napi::String::New(env, "getAllContacts"),

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ listener.remove = () => {
1212
contacts.removeListener()
1313
}
1414

15+
listener.isListening = () => contacts.isListening()
16+
1517
const optionalProperties = [
1618
'jobTitle',
1719
'departmentName',

test/module.spec.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,23 @@ describe('node-mac-contacts', () => {
203203
})
204204

205205
describe('listener', () => {
206+
afterEach(() => {
207+
if (listener.isListening()) {
208+
listener.remove()
209+
}
210+
})
211+
206212
it('throws when trying to remove a nonexistent listener', () => {
207213
expect(() => {
208214
listener.remove()
209-
}).to.throw(/No observers are currently listening/)
215+
}).to.throw(/No observers are currently observing/)
216+
})
217+
218+
it('throws when trying to setup an already-existent listener', () => {
219+
expect(() => {
220+
listener.setup()
221+
listener.setup()
222+
}).to.throw(/An observer already observing/)
210223
})
211224

212225
it('emits an event when the contact is changed', (done) => {
@@ -222,7 +235,6 @@ describe('node-mac-contacts', () => {
222235
})
223236

224237
listener.once('contact-changed', () => {
225-
listener.remove()
226238
done()
227239
})
228240
})

0 commit comments

Comments
 (0)