feat(adb): use sticky event emitter for AdbServerClient.DeviceObserver.prototype.onListChange event

This commit is contained in:
Simon Chan 2025-03-01 03:14:45 +08:00
parent 4deb890b20
commit 90cc63ee4f
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
5 changed files with 40 additions and 13 deletions

View file

@ -1,5 +1,5 @@
import type { DeviceObserver } from "@yume-chan/adb"; import type { DeviceObserver } from "@yume-chan/adb";
import { EventEmitter } from "@yume-chan/event"; import { EventEmitter, StickyEventEmitter } from "@yume-chan/event";
import { import {
AdbDaemonWebUsbDevice, AdbDaemonWebUsbDevice,
@ -34,7 +34,7 @@ export class AdbDaemonWebUsbDeviceObserver
#onDeviceRemove = new EventEmitter<AdbDaemonWebUsbDevice[]>(); #onDeviceRemove = new EventEmitter<AdbDaemonWebUsbDevice[]>();
onDeviceRemove = this.#onDeviceRemove.event; onDeviceRemove = this.#onDeviceRemove.event;
#onListChange = new EventEmitter<AdbDaemonWebUsbDevice[]>(); #onListChange = new StickyEventEmitter<AdbDaemonWebUsbDevice[]>();
onListChange = this.#onListChange.event; onListChange = this.#onListChange.event;
current: AdbDaemonWebUsbDevice[] = []; current: AdbDaemonWebUsbDevice[] = [];

View file

@ -1,4 +1,4 @@
import { EventEmitter } from "@yume-chan/event"; import { EventEmitter, StickyEventEmitter } from "@yume-chan/event";
import { Ref } from "../utils/index.js"; import { Ref } from "../utils/index.js";
@ -95,6 +95,17 @@ export class AdbServerDeviceObserverOwner {
throw options.signal.reason; throw options.signal.reason;
} }
const onDeviceAdd = new EventEmitter<AdbServerClient.Device[]>();
const onDeviceRemove = new EventEmitter<AdbServerClient.Device[]>();
const onListChange = new StickyEventEmitter<AdbServerClient.Device[]>();
const onError = new StickyEventEmitter<Error>();
const observer = { onDeviceAdd, onDeviceRemove, onListChange, onError };
// Register `observer` before `#connect`.
// Because `#connect` might immediately receive some data
// and want to trigger observers
this.#observers.push(observer);
this.#stream ??= this.#connect(); this.#stream ??= this.#connect();
const stream = await this.#stream; const stream = await this.#stream;
@ -103,14 +114,6 @@ export class AdbServerDeviceObserverOwner {
throw options.signal.reason; throw options.signal.reason;
} }
const onDeviceAdd = new EventEmitter<AdbServerClient.Device[]>();
const onDeviceRemove = new EventEmitter<AdbServerClient.Device[]>();
const onListChange = new EventEmitter<AdbServerClient.Device[]>();
const onError = new EventEmitter<Error>();
const observer = { onDeviceAdd, onDeviceRemove, onListChange, onError };
this.#observers.push(observer);
const ref = new Ref(options); const ref = new Ref(options);
const stop = async () => { const stop = async () => {

View file

@ -62,7 +62,7 @@ export class EventEmitter<TEvent, TResult = unknown> implements Disposable {
fire(e: TEvent) { fire(e: TEvent) {
for (const info of this.listeners.slice()) { for (const info of this.listeners.slice()) {
info.listener.apply(info.thisArg, [e, ...info.args]); info.listener.call(info.thisArg, e, ...info.args);
} }
} }

View file

@ -1,4 +1,5 @@
export * from "./disposable.js"; export * from "./disposable.js";
export * from "./event.js";
export * from "./event-emitter.js"; export * from "./event-emitter.js";
export * from "./event.js";
export * from "./sticky-event-emitter.js";
export * from "./utils.js"; export * from "./utils.js";

View file

@ -0,0 +1,23 @@
import { EventEmitter, type EventListenerInfo } from "./event-emitter.js";
import type { RemoveEventListener } from "./event.js";
export class StickyEventEmitter<TEvent, TResult = unknown> extends EventEmitter<
TEvent,
TResult
> {
#value: TEvent | undefined;
protected override addEventListener(
info: EventListenerInfo<TEvent, TResult>,
): RemoveEventListener {
if (this.#value) {
info.listener.call(info.thisArg, this.#value, ...info.args);
}
return super.addEventListener(info);
}
override fire(e: TEvent): void {
this.#value = e;
super.fire(e);
}
}