Skip to content

Commit

Permalink
feat(platform): add WA_IS_TOUCH, WA_IS_WEBKIT, WA_IS_ANDROID, `…
Browse files Browse the repository at this point in the history
…WA_IS_IOS`, `WA_IS_MOBILE` (#944)
  • Loading branch information
splincode authored Oct 10, 2024
1 parent af93c36 commit 70543f1
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 16 deletions.
47 changes: 47 additions & 0 deletions libs/platform/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
require('karma-coverage'),
],
client: {
clearContext: true, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
preprocessors: {
'src/**/*.js': ['coverage'],
},
coverageReporter: {
type: 'html',
dir: require('node:path').join(__dirname, '../../coverage/payment-request'),
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
singleRun: true,
customLaunchers: {
ChromeHeadless: {
base: 'Chrome',
flags: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--remote-debugging-port=9222',
],
},
},
});
};
38 changes: 22 additions & 16 deletions libs/platform/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@
"sourceRoot": "libs/platform",
"projectType": "library",
"targets": {
"test": {
"executor": "@angular-devkit/build-angular:karma",
"outputs": ["{workspaceRoot}/coverage/{projectName}"],
"options": {
"main": "libs/{projectName}/test.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "libs/{projectName}/karma.conf.js",
"codeCoverage": true,
"browsers": "ChromeHeadless"
}
},
"build": {
"executor": "@nx/rollup:rollup",
"executor": "@angular-devkit/build-angular:ng-packagr",
"outputs": ["{workspaceRoot}/dist/{projectName}"],
"options": {
"project": "{projectRoot}/package.json",
"outputPath": "dist/{projectName}",
"entryFile": "{projectRoot}/src/index.ts",
"tsConfig": "{projectRoot}/tsconfig.lib.json",
"format": ["esm", "cjs"],
"compiler": "tsc",
"external": "all",
"assets": [
{
"glob": "{projectRoot}/README.md",
"input": ".",
"output": "."
}
]
}
"tsConfig": "tsconfig.build.json",
"project": "libs/{projectName}/ng-package.json"
},
"dependsOn": [
{
"target": "build",
"params": "forward",
"dependencies": true
}
]
},
"publish": {
"executor": "nx:run-commands",
Expand Down
7 changes: 7 additions & 0 deletions libs/platform/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
export * from './is-android';
export * from './is-apple';
export * from './is-edge';
export * from './is-firefox';
export * from './is-ios';
export * from './is-mobile';
export * from './is-safari';
export * from './is-touch';
export * from './is-webkit';
8 changes: 8 additions & 0 deletions libs/platform/src/is-android.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {inject, InjectionToken} from '@angular/core';

import {WA_IS_IOS} from './is-ios';
import {WA_IS_MOBILE} from './is-mobile';

export const WA_IS_ANDROID = new InjectionToken<boolean>('', {
factory: () => inject(WA_IS_MOBILE) && !inject(WA_IS_IOS),
});
3 changes: 3 additions & 0 deletions libs/platform/src/is-edge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isEdge(userAgent: string): boolean {
return userAgent.toLowerCase().includes('edge');
}
3 changes: 3 additions & 0 deletions libs/platform/src/is-firefox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isFirefox(userAgent: string): boolean {
return userAgent.toLowerCase().includes('firefox');
}
7 changes: 7 additions & 0 deletions libs/platform/src/is-ios.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import {inject, InjectionToken} from '@angular/core';
import {WA_NAVIGATOR} from '@ng-web-apis/common';

import {isApple} from './is-apple';

const IOS_REG_EXP = /ipad|iphone|ipod/;
Expand All @@ -8,3 +11,7 @@ export function isIos(navigator: Navigator): boolean {
(isApple(navigator) && navigator.maxTouchPoints > 1)
);
}

export const WA_IS_IOS = new InjectionToken<boolean>('', {
factory: () => isIos(inject(WA_NAVIGATOR)),
});
16 changes: 16 additions & 0 deletions libs/platform/src/is-mobile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {inject, InjectionToken} from '@angular/core';
import {WA_USER_AGENT} from '@ng-web-apis/common';

// https://stackoverflow.com/a/11381730/2706426 http://detectmobilebrowsers.com/
const firstRegex =
// eslint-disable-next-line sonarjs/regex-complexity
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series([46])0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/;
const secondRegex =
// eslint-disable-next-line sonarjs/regex-complexity
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br([ev])w|bumb|bw-([nu])|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do([cp])o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly([-_])|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-([mpt])|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c([- _agpst])|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac([ \-/])|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja([tv])a|jbro|jemu|jigs|kddi|keji|kgt([ /])|klon|kpt |kwc-|kyo([ck])|le(no|xi)|lg( g|\/([klu])|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t([- ov])|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30([02])|n50([025])|n7(0([01])|10)|ne(([cm])-|on|tf|wf|wg|wt)|nok([6i])|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan([adt])|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c([-01])|47|mc|nd|ri)|sgh-|shar|sie([-m])|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel([im])|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c([- ])|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/;

export const WA_IS_MOBILE = new InjectionToken<boolean>('', {
factory: () =>
firstRegex.test(inject(WA_USER_AGENT).toLowerCase()) ||
secondRegex.test(inject(WA_USER_AGENT).slice(0, 4).toLowerCase()),
});
15 changes: 15 additions & 0 deletions libs/platform/src/is-safari.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// TODO: Drop change to Document in v5
export function isSafari({ownerDocument: doc}: Element): boolean {
const win = doc?.defaultView as unknown as Window & {safari?: any};

const isMacOsSafari =
win.safari !== undefined &&
win.safari?.pushNotification?.toString() === '[object SafariRemoteNotification]';

const isIosSafari =
!!win.navigator?.vendor?.includes('Apple') &&
!win.navigator?.userAgent?.includes('CriOS') &&
!win.navigator?.userAgent?.includes('FxiOS');

return isMacOsSafari || isIosSafari;
}
15 changes: 15 additions & 0 deletions libs/platform/src/is-touch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type {Signal} from '@angular/core';
import {inject, InjectionToken} from '@angular/core';
import {toSignal} from '@angular/core/rxjs-interop';
import {WA_WINDOW} from '@ng-web-apis/common';
import {fromEvent, map} from 'rxjs';

export const WA_IS_TOUCH = new InjectionToken<Signal<boolean>>('', {
factory: () => {
const media = inject(WA_WINDOW).matchMedia('(pointer: coarse)');

return toSignal(fromEvent(media, 'change').pipe(map(() => media.matches)), {
initialValue: media.matches,
});
},
});
6 changes: 6 additions & 0 deletions libs/platform/src/is-webkit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {inject, InjectionToken} from '@angular/core';
import {WA_WINDOW} from '@ng-web-apis/common';

export const WA_IS_WEBKIT = new InjectionToken('', {
factory: () => !!inject<any>(WA_WINDOW)?.webkitConvertPointFromNodeToPage,
});
13 changes: 13 additions & 0 deletions libs/platform/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'zone.js';
import 'zone.js/testing';

import {getTestBed} from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
72 changes: 72 additions & 0 deletions libs/platform/tests/browsers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {isEdge, isFirefox, isSafari} from '@ng-web-apis/platform';

window.onbeforeunload = jasmine.createSpy();

describe('Browsers', () => {
it('isEdge', () => {
expect(isEdge('edge')).toBe(true);
});

it('isFirefox', () => {
expect(isFirefox('firefox')).toBe(true);
expect(isFirefox('Firefox')).toBe(true);
});

describe('isSafari', () => {
it('detect by SafariRemoteNotification', () => {
expect(
isSafari({
ownerDocument: {
defaultView: {
safari: {
pushNotification: new (class {
public toString(): string {
return '[object SafariRemoteNotification]';
}
})(),
},
},
},
} as unknown as Element),
).toBe(true);

expect(
isSafari({
ownerDocument: {
defaultView: {navigator: {}},
},
} as unknown as Element),
).toBe(false);
});

it('detect by vendor', () => {
expect(
isSafari({
ownerDocument: {
defaultView: {
navigator: {
vendor: 'Apple Computer, Inc.',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15',
},
},
},
} as unknown as Element),
).toBe(true);

expect(
isSafari({
ownerDocument: {
defaultView: {
navigator: {
vendor: 'Google Inc.',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
},
},
},
} as unknown as Element),
).toBe(false);
});
});
});
5 changes: 5 additions & 0 deletions libs/platform/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.spec.json",
"include": ["**/*.spec.ts", "./test.ts", "**/*.d.ts"],
"files": ["./test.ts"]
}

0 comments on commit 70543f1

Please sign in to comment.