Skip to content

Observable and EventData cleanup #10181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 37 additions & 42 deletions packages/core/abortcontroller/abortsignal.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,75 @@

import { Observable } from '../data/observable';

// Known Limitation
// Use `any` because the type of `AbortSignal` in `lib.dom.d.ts` is wrong and
// to make assignable our `AbortSignal` into that.
// https://github.com/Microsoft/TSJS-lib-generator/pull/623
type Events = {
abort: any // Event & Type<"abort">
}
abort: any; // Event & Type<"abort">
};
type EventAttributes = {
onabort: any // Event & Type<"abort">
}
onabort: any; // Event & Type<"abort">
};

/**
* The signal class.
* @see https://dom.spec.whatwg.org/#abortsignal
*/
export default class AbortSignal extends Observable {
/**
* AbortSignal cannot be constructed directly.
*/
public constructor() {
super()
}
/**
* AbortSignal cannot be constructed directly.
*/
public constructor() {
super();
}

/**
* Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise.
*/
public get aborted(): boolean {
const aborted = abortedFlags.get(this)
if (typeof aborted !== "boolean") {
throw new TypeError(
`Expected 'this' to be an 'AbortSignal' object, but got ${
this === null ? "null" : typeof this
}`,
)
}
return aborted
}
/**
* Returns `true` if this `AbortSignal`'s `AbortController` has signaled to abort, and `false` otherwise.
*/
public get aborted(): boolean {
const aborted = abortedFlags.get(this);
if (typeof aborted !== 'boolean') {
throw new TypeError(`Expected 'this' to be an 'AbortSignal' object, but got ${this === null ? 'null' : typeof this}`);
}
return aborted;
}
}

/**
* Create an AbortSignal object.
*/
export function createAbortSignal(): AbortSignal {
const signal = new AbortSignal();
abortedFlags.set(signal, false)
return signal
const signal = new AbortSignal();
abortedFlags.set(signal, false);
return signal;
}

/**
* Abort a given signal.
*/
export function abortSignal(signal: AbortSignal): void {
if (abortedFlags.get(signal) !== false) {
return
}
if (abortedFlags.get(signal) !== false) {
return;
}

abortedFlags.set(signal, true)
signal.notify({ eventName: "abort", type: "abort" })
abortedFlags.set(signal, true);
signal.notify({ eventName: 'abort', type: 'abort' });
}

/**
* Aborted flag for each instances.
*/
const abortedFlags = new WeakMap<AbortSignal, boolean>()
const abortedFlags = new WeakMap<AbortSignal, boolean>();

// Properties should be enumerable.
Object.defineProperties(AbortSignal.prototype, {
aborted: { enumerable: true },
})
aborted: { enumerable: true },
});

// `toString()` should return `"[object AbortSignal]"`
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol") {
Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {
configurable: true,
value: "AbortSignal",
})
}
if (typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol') {
Object.defineProperty(AbortSignal.prototype, Symbol.toStringTag, {
configurable: true,
value: 'AbortSignal',
});
}
2 changes: 1 addition & 1 deletion packages/core/accessibility/accessibility-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const accessibilityPerformEscapeEvent = 'accessibilityPerformEscape';
* @param {boolean} receivedFocus
* @param {boolean} lostFocus
*/
export function notifyAccessibilityFocusState(view: Partial<View>, receivedFocus: boolean, lostFocus: boolean): void {
export function notifyAccessibilityFocusState(view: View, receivedFocus: boolean, lostFocus: boolean): void {
if (!receivedFocus && !lostFocus) {
return;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/core/accessibility/font-scale.android.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ApplicationEventData } from '../application';
import * as Application from '../application';
import { FontScaleCategory, getClosestValidFontScale } from './font-scale-common';
export * from './font-scale-common';
Expand All @@ -12,7 +13,7 @@ function fontScaleChanged(origFontScale: number) {
eventName: Application.fontScaleChangedEvent,
object: Application,
newValue: currentFontScale,
});
} as ApplicationEventData);
}
}

Expand Down
14 changes: 7 additions & 7 deletions packages/core/accessibility/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export * from './font-scale';

let clickableRolesMap = new Set<string>();

let lastFocusedView: WeakRef<Partial<View>>;
function accessibilityEventHelper(view: Partial<View>, eventType: number) {
let lastFocusedView: WeakRef<View>;
function accessibilityEventHelper(view: View, eventType: number) {
const eventName = accessibilityEventTypeMap.get(eventType);
if (!isAccessibilityServiceEnabled()) {
if (Trace.isEnabled()) {
Expand Down Expand Up @@ -103,7 +103,7 @@ function accessibilityEventHelper(view: Partial<View>, eventType: number) {

let TNSAccessibilityDelegate: android.view.View.androidviewViewAccessibilityDelegate;

const androidViewToTNSView = new WeakMap<android.view.View, WeakRef<Partial<View>>>();
const androidViewToTNSView = new WeakMap<android.view.View, WeakRef<View>>();

let accessibilityEventMap: Map<AndroidAccessibilityEvent, number>;
let accessibilityEventTypeMap: Map<number, string>;
Expand Down Expand Up @@ -438,11 +438,11 @@ export function isAccessibilityServiceEnabled(): boolean {
return accessibilityServiceEnabled;
}

export function setupAccessibleView(view: Partial<View>): void {
export function setupAccessibleView(view: View): void {
updateAccessibilityProperties(view);
}

export function updateAccessibilityProperties(view: Partial<View>): void {
export function updateAccessibilityProperties(view: View): void {
if (!view.nativeViewProtected) {
return;
}
Expand Down Expand Up @@ -538,7 +538,7 @@ export function updateContentDescription(view: View, forceUpdate?: boolean): str
return applyContentDescription(view, forceUpdate);
}

function setAccessibilityDelegate(view: Partial<View>): void {
function setAccessibilityDelegate(view: View): void {
if (!view.nativeViewProtected) {
return;
}
Expand All @@ -564,7 +564,7 @@ function setAccessibilityDelegate(view: Partial<View>): void {
androidView.setAccessibilityDelegate(TNSAccessibilityDelegate);
}

function applyContentDescription(view: Partial<View>, forceUpdate?: boolean) {
function applyContentDescription(view: View, forceUpdate?: boolean) {
let androidView = view.nativeViewProtected as android.view.View;
if (!androidView || (androidView instanceof android.widget.TextView && !view._androidContentDescriptionUpdated)) {
return null;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/accessibility/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export * from './font-scale';
/**
* Initialize accessibility for View. This should be called on loaded-event.
*/
export function setupAccessibleView(view: Partial<View>): void;
export function setupAccessibleView(view: View): void;

/**
* Update accessibility properties on nativeView
Expand Down
20 changes: 10 additions & 10 deletions packages/core/application/application-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import '../globals';

// Types
import type * as IApplication from '.';
import { AndroidApplication, iOSApplication } from '.';
import { CssChangedEventData, DiscardedErrorEventData, LoadAppCSSEventData, UnhandledErrorEventData } from './application-interfaces';
import { EventData } from '../data/observable';
import { View } from '../ui/core/view';

// Requires
Expand Down Expand Up @@ -50,10 +50,10 @@ export function setResources(res: any) {
export const android: AndroidApplication = undefined;
export const ios: iOSApplication = undefined;

export const on = global.NativeScriptGlobals.events.on.bind(global.NativeScriptGlobals.events);
export const off = global.NativeScriptGlobals.events.off.bind(global.NativeScriptGlobals.events);
export const notify = global.NativeScriptGlobals.events.notify.bind(global.NativeScriptGlobals.events);
export const hasListeners = global.NativeScriptGlobals.events.hasListeners.bind(global.NativeScriptGlobals.events);
export const on = global.NativeScriptGlobals.events.on.bind(global.NativeScriptGlobals.events) as typeof IApplication.on;
export const off = global.NativeScriptGlobals.events.off.bind(global.NativeScriptGlobals.events) as typeof IApplication.off;
export const notify = global.NativeScriptGlobals.events.notify.bind(global.NativeScriptGlobals.events) as typeof IApplication.notify;
export const hasListeners = global.NativeScriptGlobals.events.hasListeners.bind(global.NativeScriptGlobals.events) as typeof IApplication.hasListeners;

let app: iOSApplication | AndroidApplication;
export function setApplication(instance: iOSApplication | AndroidApplication): void {
Expand All @@ -63,7 +63,7 @@ export function setApplication(instance: iOSApplication | AndroidApplication): v
}

export function livesync(rootView: View, context?: ModuleContext) {
global.NativeScriptGlobals.events.notify(<EventData>{ eventName: 'livesync', object: app });
notify({ eventName: 'livesync', object: app });
const liveSyncCore = global.__onLiveSyncCore;
let reapplyAppStyles = false;

Expand All @@ -85,7 +85,7 @@ export function livesync(rootView: View, context?: ModuleContext) {

export function setCssFileName(cssFileName: string) {
cssFile = cssFileName;
global.NativeScriptGlobals.events.notify(<CssChangedEventData>{
notify(<CssChangedEventData>{
eventName: 'cssChanged',
object: app,
cssFile: cssFileName,
Expand All @@ -98,7 +98,7 @@ export function getCssFileName(): string {

export function loadAppCss(): void {
try {
global.NativeScriptGlobals.events.notify(<LoadAppCSSEventData>{
notify(<LoadAppCSSEventData>{
eventName: 'loadAppCss',
object: app,
cssFile: getCssFileName(),
Expand Down Expand Up @@ -181,7 +181,7 @@ export function setSuspended(value: boolean): void {
}

global.__onUncaughtError = function (error: NativeScriptError) {
global.NativeScriptGlobals.events.notify(<UnhandledErrorEventData>{
notify(<UnhandledErrorEventData>{
eventName: uncaughtErrorEvent,
object: app,
android: error,
Expand All @@ -191,7 +191,7 @@ global.__onUncaughtError = function (error: NativeScriptError) {
};

global.__onDiscardedError = function (error: NativeScriptError) {
global.NativeScriptGlobals.events.notify(<DiscardedErrorEventData>{
notify(<DiscardedErrorEventData>{
eventName: discardedErrorEvent,
object: app,
error: error,
Expand Down
34 changes: 28 additions & 6 deletions packages/core/application/application-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,45 @@ export interface NativeScriptError extends Error {
}

export interface ApplicationEventData extends EventData {
/**
* UIApplication or undefined, unless otherwise specified. Prefer explicit
* properties where possible.
*/
ios?: any;
/**
* androidx.appcompat.app.AppCompatActivity or undefined, unless otherwise
* specified. Prefer explicit properties where possible.
*/
android?: any;
eventName: string;
/**
* Careful with this messy type. A significant refactor is needed to make it
* strictly extend EventData['object'], which is an Observable. It's used in
* various ways:
* - By font-scale: the Application module, typeof import('.')
* - Within index.android.ts: AndroidApplication
* - Within index.ios.ts: iOSApplication
*/
object: any;
}

export interface LaunchEventData extends ApplicationEventData {
/**
* The value stored into didFinishLaunchingWithOptions notification's
* userInfo under 'UIApplicationLaunchOptionsLocalNotificationKey';
* otherwise, null.
*/
ios: unknown;
root?: View | null;
savedInstanceState?: any /* android.os.Bundle */;
}

export interface OrientationChangedEventData extends ApplicationEventData {
android: any /* globalAndroid.app.Application */;
newValue: 'portrait' | 'landscape' | 'unknown';
}

export interface SystemAppearanceChangedEventData extends ApplicationEventData {
android: any /* globalAndroid.app.Application */;
newValue: 'light' | 'dark';
}

Expand All @@ -42,15 +65,14 @@ export interface DiscardedErrorEventData extends ApplicationEventData {
error: NativeScriptError;
}

export interface CssChangedEventData extends EventData {
export interface CssChangedEventData extends ApplicationEventData {
cssFile?: string;
cssText?: string;
}

export interface AndroidActivityEventData {
export interface AndroidActivityEventData extends ApplicationEventData {
activity: any /* androidx.appcompat.app.AppCompatActivity */;
eventName: string;
object: any;
object: any /* AndroidApplication */;
}

export interface AndroidActivityBundleEventData extends AndroidActivityEventData {
Expand Down Expand Up @@ -84,6 +106,6 @@ export interface RootViewControllerImpl {
contentController: any;
}

export interface LoadAppCSSEventData extends EventData {
export interface LoadAppCSSEventData extends ApplicationEventData {
cssFile: string;
}
26 changes: 13 additions & 13 deletions packages/core/application/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AndroidActivityBackPressedEventData, AndroidActivityBundleEventData, An

// TODO: explain why we need to this or remov it
// Use requires to ensure order of imports is maintained
const appCommon = require('./application-common');
const appCommon = require('./application-common') as typeof import('./application-common');

// First reexport so that app module is initialized.
export * from './application-common';
Expand Down Expand Up @@ -163,18 +163,18 @@ export class AndroidApplication extends Observable implements AndroidApplication
// HACK: We declare all these 'on' statements, so that they can appear in the API reference
// HACK: Do we need this? Is it useful? There are static fields to the AndroidApplication class for the event names.
export interface AndroidApplication {
on(eventNames: string, callback: (data: AndroidActivityEventData) => void, thisArg?: any);
on(event: 'activityCreated', callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any);
on(event: 'activityDestroyed', callback: (args: AndroidActivityEventData) => void, thisArg?: any);
on(event: 'activityStarted', callback: (args: AndroidActivityEventData) => void, thisArg?: any);
on(event: 'activityPaused', callback: (args: AndroidActivityEventData) => void, thisArg?: any);
on(event: 'activityResumed', callback: (args: AndroidActivityEventData) => void, thisArg?: any);
on(event: 'activityStopped', callback: (args: AndroidActivityEventData) => void, thisArg?: any);
on(event: 'saveActivityState', callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any);
on(event: 'activityResult', callback: (args: AndroidActivityResultEventData) => void, thisArg?: any);
on(event: 'activityBackPressed', callback: (args: AndroidActivityBackPressedEventData) => void, thisArg?: any);
on(event: 'activityNewIntent', callback: (args: AndroidActivityNewIntentEventData) => void, thisArg?: any);
on(event: 'activityRequestPermissions', callback: (args: AndroidActivityRequestPermissionsEventData) => void, thisArg?: any);
on(eventNames: string, callback: (data: AndroidActivityEventData) => void, thisArg?: any): void;
on(event: 'activityCreated', callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any): void;
on(event: 'activityDestroyed', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
on(event: 'activityStarted', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
on(event: 'activityPaused', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
on(event: 'activityResumed', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
on(event: 'activityStopped', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
on(event: 'saveActivityState', callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any): void;
on(event: 'activityResult', callback: (args: AndroidActivityResultEventData) => void, thisArg?: any): void;
on(event: 'activityBackPressed', callback: (args: AndroidActivityBackPressedEventData) => void, thisArg?: any): void;
on(event: 'activityNewIntent', callback: (args: AndroidActivityNewIntentEventData) => void, thisArg?: any): void;
on(event: 'activityRequestPermissions', callback: (args: AndroidActivityRequestPermissionsEventData) => void, thisArg?: any): void;
}

let androidApp: AndroidApplication;
Expand Down
Loading