Repository: NativeScript/NativeScript
File: packages/core/data/observable/index.ts (TypeScript)

Code before:
	 */
	public static removeEventListener(eventNames: string, callback?: (data: EventData) => void, thisArg?: any): void {
		thisArg = thisArg || undefined;

		if (typeof eventNames !== 'string') {
			throw new TypeError('Event name(s) must be a string.');
		}

		if (callback && typeof callback !== 'function') {
			throw new TypeError('Callback, if provided, must be function.');
		}

		const eventClass = this.name === 'Observable' ? '*' : this.name;

		for (const eventName of eventNames.trim().split(eventNamesRegex)) {
			const entries = _globalEventHandlers?.[eventClass]?.[eventName];
			if (!entries) {
				continue;
			}

			Observable.innerRemoveEventListener(entries, callback, thisArg);

			if (!entries.length) {
				// Clear all entries of this type
				delete _globalEventHandlers[eventClass][eventName];
			}

			// Clear the primary class grouping if no list are left
			const keys = Object.keys(_globalEventHandlers[eventClass]);
			if (keys.length === 0) {
				delete _globalEventHandlers[eventClass];
			}
		}
	}

	/**
	 * Please avoid using the static event-handling APIs as they will be removed
	 * in future.
	 * @deprecated
	 */
	public static addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
		once = once || undefined;
		thisArg = thisArg || undefined;

		if (typeof eventNames !== 'string') {
			throw new TypeError('Event name(s) must be a string.');
		}

		if (typeof callback !== 'function') {
			throw new TypeError('Callback must be a function.');
		}

Review comment (bug):
Global event handling will be removed?
Many plugins might break because of that.
At first i was not a fan of global events, but now with the sentry plugin it became a major part of it. Global events allow sentry to keep track of all navigations / modals / bottomsheet ... in the app and log them for easier debugging of issues

Code after:
		const eventClass = this.name === 'Observable' ? '*' : this.name;

		for (const eventName of eventNames.trim().split(eventNamesRegex)) {
			const entries = _globalEventHandlers?.[eventClass]?.[eventName];
			if (!entries) {
				continue;
			}

			Observable.innerRemoveEventListener(entries, callback, thisArg);

			if (!entries.length) {
				// Clear all entries of this type
				delete _globalEventHandlers[eventClass][eventName];
			}

			// Clear the primary class grouping if no list are left
			const keys = Object.keys(_globalEventHandlers[eventClass]);
			if (keys.length === 0) {
				delete _globalEventHandlers[eventClass];
			}
		}
	}

	/**
	 * Please avoid using the static event-handling APIs as they will be removed
	 * in future.
	 * @deprecated
	 */
	public static addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
		once = once || undefined;
		thisArg = thisArg || undefined;

		if (typeof eventNames !== 'string') {
			throw new TypeError('Event name(s) must be a string.');
		}

		if (typeof callback !== 'function') {
			throw new TypeError('Callback must be a function.');
		}

		const eventClass = this.name === 'Observable' ? '*' : this.name;
		if (!_globalEventHandlers[eventClass]) {
			_globalEventHandlers[eventClass] = {};
		}

		for (const eventName of eventNames.trim().split(eventNamesRegex)) {
			if (!_globalEventHandlers[eventClass][eventName]) {
				_globalEventHandlers[eventClass][eventName] = [];
			}
			if (Observable._indexOfListener(_globalEventHandlers[eventClass][eventName], callback, thisArg) !== -1) {
				// Already added.