Skip to content
Open
94 changes: 74 additions & 20 deletions packages/core/application/application-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { NavigationEntry } from '../ui/frame/frame-interfaces';
import type { StyleScope } from '../ui/styling/style-scope';
import type { AndroidApplication as AndroidApplicationType, iOSApplication as iOSApplicationType } from '.';
import type { ApplicationEventData, CssChangedEventData, DiscardedErrorEventData, FontScaleChangedEventData, InitRootViewEventData, LaunchEventData, LoadAppCSSEventData, NativeScriptError, OrientationChangedEventData, SystemAppearanceChangedEventData, LayoutDirectionChangedEventData, UnhandledErrorEventData } from './application-interfaces';
import { ApplicationEventNames, ApplicationEventNameType } from './application-event-names';
import { readyInitAccessibilityCssHelper, readyInitFontScale } from '../accessibility/accessibility-common';
import { getAppMainEntry, isAppInBackground, setAppInBackground, setAppMainEntry } from './helpers-common';
import { getNativeScriptGlobals } from '../globals/global-utils';
Expand Down Expand Up @@ -179,26 +180,7 @@ interface ApplicationEvents {
on(event: 'fontScaleChanged', callback: (args: FontScaleChangedEventData) => void, thisArg?: any): void;
}

export class ApplicationCommon {
readonly launchEvent = 'launch';
readonly suspendEvent = 'suspend';
readonly displayedEvent = 'displayed';
readonly backgroundEvent = 'background';
readonly foregroundEvent = 'foreground';
readonly resumeEvent = 'resume';
readonly exitEvent = 'exit';
readonly lowMemoryEvent = 'lowMemory';
readonly uncaughtErrorEvent = 'uncaughtError';
readonly discardedErrorEvent = 'discardedError';
readonly orientationChangedEvent = 'orientationChanged';
readonly systemAppearanceChangedEvent = 'systemAppearanceChanged';
readonly layoutDirectionChangedEvent = 'layoutDirectionChanged';
readonly fontScaleChangedEvent = 'fontScaleChanged';
readonly livesyncEvent = 'livesync';
readonly loadAppCssEvent = 'loadAppCss';
readonly cssChangedEvent = 'cssChanged';
readonly initRootViewEvent = 'initRootView';

export class ApplicationCommon implements ApplicationEventNameType {
// Expose statically for backwards compat on AndroidApplication.on etc.
/**
* @deprecated Use `Application.android.on()` instead.
Expand Down Expand Up @@ -277,6 +259,78 @@ export class ApplicationCommon {
};
}

get launchEvent() {
return ApplicationEventNames.launchEvent;
}

get suspendEvent() {
return ApplicationEventNames.suspendEvent;
}

get displayedEvent() {
return ApplicationEventNames.displayedEvent;
}

get backgroundEvent() {
return ApplicationEventNames.backgroundEvent;
}

get foregroundEvent() {
return ApplicationEventNames.foregroundEvent;
}

get resumeEvent() {
return ApplicationEventNames.resumeEvent;
}

get exitEvent() {
return ApplicationEventNames.exitEvent;
}

get lowMemoryEvent() {
return ApplicationEventNames.lowMemoryEvent;
}

get uncaughtErrorEvent() {
return ApplicationEventNames.uncaughtErrorEvent;
}

get discardedErrorEvent() {
return ApplicationEventNames.discardedErrorEvent;
}

get orientationChangedEvent() {
return ApplicationEventNames.orientationChangedEvent;
}

get systemAppearanceChangedEvent() {
return ApplicationEventNames.systemAppearanceChangedEvent;
}

get layoutDirectionChangedEvent() {
return ApplicationEventNames.layoutDirectionChangedEvent;
}

get fontScaleChangedEvent() {
return ApplicationEventNames.fontScaleChangedEvent;
}

get livesyncEvent() {
return ApplicationEventNames.livesyncEvent;
}

get loadAppCssEvent() {
return ApplicationEventNames.loadAppCssEvent;
}

get cssChangedEvent() {
return ApplicationEventNames.cssChangedEvent;
}

get initRootViewEvent() {
return ApplicationEventNames.initRootViewEvent;
}

/**
* @internal
*/
Expand Down
23 changes: 23 additions & 0 deletions packages/core/application/application-event-names.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const ApplicationEventNames = Object.freeze({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NathanWalker I forgot to mention this but I put application event names into their own separate module and Application instance accesses them using getters.
At the same time, Application implements the type of the ApplicationEventNames to ensure that no getter is accidentally missed.

If this change looks ugly, we can definitely revert it.

launchEvent: 'launch',
suspendEvent: 'suspend',
displayedEvent: 'displayed',
backgroundEvent: 'background',
foregroundEvent: 'foreground',
resumeEvent: 'resume',
exitEvent: 'exit',
lowMemoryEvent: 'lowMemory',
uncaughtErrorEvent: 'uncaughtError',
discardedErrorEvent: 'discardedError',
orientationChangedEvent: 'orientationChanged',
systemAppearanceChangedEvent: 'systemAppearanceChanged',
layoutDirectionChangedEvent: 'layoutDirectionChanged',
fontScaleChangedEvent: 'fontScaleChanged',
livesyncEvent: 'livesync',
loadAppCssEvent: 'loadAppCss',
cssChangedEvent: 'cssChanged',
initRootViewEvent: 'initRootView',
});

export type ApplicationEventNameType = typeof ApplicationEventNames;
export type ApplicationEventName = ApplicationEventNameType[keyof ApplicationEventNameType];
14 changes: 1 addition & 13 deletions packages/core/application/application.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
setA11yEnabled,
} from '../accessibility/accessibility-common';
import { androidGetForegroundActivity, androidGetStartActivity, androidSetForegroundActivity, androidSetStartActivity, applyContentDescription } from './helpers';
import { getImageFetcher, getNativeApp, getRootView, initImageCache, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setNativeApp, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
import { getImageFetcher, getNativeApp, getRootView, initImageCache, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setNativeApp, setRootView } from './helpers-common';
import { getNativeScriptGlobals } from '../globals/global-utils';
import type { AndroidApplication as IAndroidApplication } from './application';
import lazy from '../utils/lazy';
Expand Down Expand Up @@ -1447,18 +1447,6 @@ function setAccessibilityDelegate(view: View): void {
androidView.setAccessibilityDelegate(TNSAccessibilityDelegate);
}

const applicationEvents: string[] = [Application.orientationChangedEvent, Application.systemAppearanceChangedEvent];
function toggleApplicationEventListeners(toAdd: boolean, callback: (args: ApplicationEventData) => void) {
for (const eventName of applicationEvents) {
if (toAdd) {
Application.on(eventName, callback);
} else {
Application.off(eventName, callback);
}
}
}
setToggleApplicationEventListenersCallback(toggleApplicationEventListeners);

setApplicationPropertiesCallback(() => {
return {
orientation: Application.orientation(),
Expand Down
14 changes: 1 addition & 13 deletions packages/core/application/application.ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
enforceArray,
} from '../accessibility/accessibility-common';
import { CoreTypes } from '../core-types';
import { getiOSWindow, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setiOSWindow, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
import { getiOSWindow, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setiOSWindow, setRootView } from './helpers-common';

@NativeClass
class NotificationObserver extends NSObject {
Expand Down Expand Up @@ -1754,18 +1754,6 @@ export function initAccessibilityCssHelper(): void {
}
setInitAccessibilityCssHelper(initAccessibilityCssHelper);

const applicationEvents: string[] = [Application.orientationChangedEvent, Application.systemAppearanceChangedEvent];
function toggleApplicationEventListeners(toAdd: boolean, callback: (args: ApplicationEventData) => void) {
for (const eventName of applicationEvents) {
if (toAdd) {
Application.on(eventName, callback);
} else {
Application.off(eventName, callback);
}
}
}
setToggleApplicationEventListenersCallback(toggleApplicationEventListeners);

setApplicationPropertiesCallback(() => {
return {
orientation: Application.orientation(),
Expand Down
11 changes: 0 additions & 11 deletions packages/core/application/helpers-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,6 @@ export function setAppMainEntry(entry: any /* NavigationEntry */) {
_appMainEntry = entry;
}

// Aids avoiding circular dependencies by allowing the application event listeners to be toggled
let _toggleApplicationEventListenersHandler: (toAdd: boolean, callback: (args: any) => void) => void;
export function toggleApplicationEventListeners(toAdd: boolean, callback: (args: any) => void) {
if (_toggleApplicationEventListenersHandler) {
_toggleApplicationEventListenersHandler(toAdd, callback);
}
}
export function setToggleApplicationEventListenersCallback(callback: (toAdd: boolean, callback: (args: any) => void) => void) {
_toggleApplicationEventListenersHandler = callback;
}

// Aids avoiding circular dependencies by allowing the application properties to be retrieved
type ApplicationPropertyValues = { orientation: 'portrait' | 'landscape' | 'unknown'; systemAppearance: 'dark' | 'light' | null };
let _applicationPropertiesCallback: () => ApplicationPropertyValues;
Expand Down
1 change: 0 additions & 1 deletion packages/core/application/helpers.android.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { SDK_VERSION } from '../utils/constants';
import { getNativeApp, updateA11yPropertiesCallback } from './helpers-common';
import { AccessibilityRole, AccessibilityState } from '../accessibility/accessibility-common';
import { Trace } from '../trace';
Expand Down
118 changes: 8 additions & 110 deletions packages/core/css-mediaquery/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,118 +1,16 @@
import { MediaQueryType, matchQuery, parseQuery } from '.';
import { checkIfMediaQueryMatches } from '.';
import { Screen } from '../platform';

describe('css-mediaquery', () => {
describe('parseQuery', () => {
it('should parse media queries without expressions', () => {
expect(parseQuery('screen')).toEqual([
{
inverse: false,
type: MediaQueryType.screen,
features: [],
},
]);
const { widthDIPs } = Screen.mainScreen;

expect(parseQuery('not screen')).toEqual([
{
inverse: true,
type: MediaQueryType.screen,
features: [],
},
]);
describe('checkIfMediaQueryMatches', () => {
it('should return true for a correct match', () => {
expect(checkIfMediaQueryMatches(`only screen and (max-width: ${widthDIPs})`)).toBe(true);
});

it('should throw a SyntaxError when a media query is invalid', () => {
expect(() => parseQuery('some crap')).toThrow(SyntaxError);
expect(() => parseQuery('48em')).toThrow(SyntaxError);
expect(() => parseQuery('screen and crap')).toThrow(SyntaxError);
expect(() => parseQuery('screen and (48em)')).toThrow(SyntaxError);
expect(() => parseQuery('screen and (foo:)')).toThrow(SyntaxError);
expect(() => parseQuery('()')).toThrow(SyntaxError);
expect(() => parseQuery('(foo) (bar)')).toThrow(SyntaxError);
expect(() => parseQuery('(foo:) and (bar)')).toThrow(SyntaxError);
});
});

describe('matchQuery', () => {
describe('Equality check', () => {
it('orientation: should return true for a correct match (===)', () => {
expect(matchQuery('(orientation: portrait)', { orientation: 'portrait' })).toBe(true);
});

it('orientation: should return false for an incorrect match (===)', () => {
expect(matchQuery('(orientation: landscape)', { orientation: 'portrait' })).toBe(false);
});

it('prefers-color-scheme: should return true for a correct match (===)', () => {
expect(matchQuery('(prefers-color-scheme: dark)', { 'prefers-color-scheme': 'dark' })).toBe(true);
});

it('prefers-color-scheme: should return false for an incorrect match (===)', () => {
expect(matchQuery('(prefers-color-scheme: light)', { 'prefers-color-scheme': 'dark' })).toBe(false);
});

it('width: should return true for a correct match', () => {
expect(matchQuery('(width: 800px)', { width: 800 })).toBe(true);
});

it('width: should return false for an incorrect match', () => {
expect(matchQuery('(width: 800px)', { width: 900 })).toBe(false);
});
});

describe('Type', () => {
it('should return true for a correct match', () => {
expect(matchQuery('screen', { type: MediaQueryType.screen })).toBe(true);
});

it('should return false for an incorrect match', () => {
expect(
matchQuery('screen and (orientation: portrait)', {
type: MediaQueryType.print,
orientation: 'portrait',
}),
).toBe(false);
});

it('should return false for a media query without a type when type is specified in the value object', () => {
expect(matchQuery('(min-width: 500px)', { type: MediaQueryType.screen })).toBe(false);
});

it('should return true for a media query without a type when type is not specified in the value object', () => {
expect(matchQuery('(min-width: 500px)', { width: 700 })).toBe(true);
});
});

describe('Not', () => {
it('should return false when theres a match on a `not` query', () => {
expect(
matchQuery('not screen and (orientation: portrait)', {
type: MediaQueryType.screen,
orientation: 'landscape',
}),
).toBe(false);
});

it('should not disrupt an OR query', () => {
expect(
matchQuery('not screen and (color), screen and (min-height: 48em)', {
type: MediaQueryType.screen,
height: 1000,
}),
).toBe(true);
});

it('should return false for when type === all', () => {
expect(
matchQuery('not all and (min-width: 48em)', {
type: MediaQueryType.all,
width: 1000,
}),
).toBe(false);
});

it('should return true for inverted value', () => {
expect(matchQuery('not screen and (min-width: 48px)', { width: 24 })).toBe(true);
});
it('should return false for an incorrect match', () => {
expect(checkIfMediaQueryMatches(`only screen and (max-width: ${widthDIPs - 1})`)).toBe(false);
});
});
});
Loading