Skip to content

Commit

Permalink
[core] Add listenerCount function to the new EventEmitter (#27876)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsapeta committed Mar 27, 2024
1 parent 04b40fb commit 115be64
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 4 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/expo-modules-core/build/web/CoreModule.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/expo-modules-core/build/web/CoreModule.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion packages/expo-modules-core/common/cpp/EventEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ void emitEvent(jsi::Runtime &runtime, const jsi::Object &emitter, const std::str
}
}

size_t getListenerCount(jsi::Runtime &runtime, const jsi::Object &emitter, const std::string &eventName) {
if (NativeState::Shared state = NativeState::get(runtime, emitter, false)) {
return state->listeners.listenersCount(eventName);
}
return 0;
}

jsi::Value createEventSubscription(jsi::Runtime &runtime, const std::string &eventName, const jsi::Object &emitter, const jsi::Function &listener) {
jsi::Object subscription(runtime);
jsi::PropNameID removeProp = jsi::PropNameID::forAscii(runtime, "remove", 6);
Expand Down Expand Up @@ -213,7 +220,7 @@ void installClass(jsi::Runtime &runtime) {
std::string eventName = args[0].asString(runtime).utf8(runtime);

// Unwrap `this` object if it's a lazy object (e.g. native module).
const jsi::Object &thisObject = thisValue.getObject(runtime);
const jsi::Object &thisObject = LazyObject::unwrapObjectIfNecessary(runtime, thisValue.getObject(runtime));

// Make a new pointer that skips the first argument which is the event name.
const jsi::Value *eventArgs = count > 1 ? &args[1] : nullptr;
Expand All @@ -222,15 +229,26 @@ void installClass(jsi::Runtime &runtime) {
return jsi::Value::undefined();
};

jsi::HostFunctionType listenerCountHost = [](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
std::string eventName = args[0].asString(runtime).utf8(runtime);

// Unwrap `this` object if it's a lazy object (e.g. native module).
const jsi::Object &thisObject = LazyObject::unwrapObjectIfNecessary(runtime, thisValue.getObject(runtime));

return jsi::Value((int)getListenerCount(runtime, thisObject, eventName));
};

jsi::PropNameID addListenerProp = jsi::PropNameID::forAscii(runtime, "addListener", 11);
jsi::PropNameID removeListenerProp = jsi::PropNameID::forAscii(runtime, "removeListener", 14);
jsi::PropNameID removeAllListenersProp = jsi::PropNameID::forAscii(runtime, "removeAllListeners", 18);
jsi::PropNameID emitProp = jsi::PropNameID::forAscii(runtime, "emit", 4);
jsi::PropNameID listenerCountProp = jsi::PropNameID::forAscii(runtime, "listenerCount", 13);

prototype.setProperty(runtime, addListenerProp, jsi::Function::createFromHostFunction(runtime, addListenerProp, 2, addListenerHost));
prototype.setProperty(runtime, removeListenerProp, jsi::Function::createFromHostFunction(runtime, removeListenerProp, 2, removeListenerHost));
prototype.setProperty(runtime, removeAllListenersProp, jsi::Function::createFromHostFunction(runtime, removeAllListenersProp, 1, removeAllListenersHost));
prototype.setProperty(runtime, emitProp, jsi::Function::createFromHostFunction(runtime, emitProp, 2, emit));
prototype.setProperty(runtime, listenerCountProp, jsi::Function::createFromHostFunction(runtime, listenerCountProp, 1, listenerCountHost));

common::getCoreObject(runtime)
.setProperty(runtime, "EventEmitter", eventEmitterClass);
Expand Down
1 change: 1 addition & 0 deletions packages/expo-modules-core/common/cpp/EventEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Listeners {
friend void removeListener(jsi::Runtime &runtime, const jsi::Object &emitter, const std::string &eventName, const jsi::Function &listener);
friend void removeAllListeners(jsi::Runtime &runtime, const jsi::Object &emitter, const std::string &eventName);
friend void emitEvent(jsi::Runtime &runtime, const jsi::Object &emitter, const std::string &eventName, const jsi::Value *args, size_t count);
friend size_t getListenerCount(jsi::Runtime &runtime, const jsi::Object &emitter, const std::string &eventName);

/**
Type of the list containing listeners for the specific event name.
Expand Down
32 changes: 32 additions & 0 deletions packages/expo-modules-core/ios/Tests/EventEmitterSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,38 @@ final class EventEmitterSpec: ExpoSpec {
expect(args[2]) == 24
}

it("returns listener count with listeners added") {
let listenerCount = try runtime.eval([
"emitter = new expo.EventEmitter()",
"emitter.addListener('test', () => {})",
"emitter.addListener('test', () => {})",
"emitter.listenerCount('test')"
])
expect(listenerCount.kind) == .number
expect(try listenerCount.asInt()) == 2
}

it("returns listener count without any listeners") {
let listenerCount = try runtime.eval([
"emitter = new expo.EventEmitter()",
"emitter.listenerCount('test')"
])
expect(listenerCount.kind) == .number
expect(try listenerCount.asInt()) == 0
}

it("returns listener count for the proper event") {
let listenerCount = try runtime.eval([
"emitter = new expo.EventEmitter()",
"emitter.addListener('test1', () => {})",
"emitter.addListener('test1', () => {})",
"emitter.addListener('test2', () => {})",
"emitter.listenerCount('test2')"
])
expect(listenerCount.kind) == .number
expect(try listenerCount.asInt()) == 1
}

it("calls startObserving on addListener") {
var calls: Int = 0
let eventName = "testEvent"
Expand Down

0 comments on commit 115be64

Please sign in to comment.