├── .babelrc
├── .eslintrc
├── .gitignore
├── .travis.yml
├── README.md
├── package.json
├── public
└── index.html
├── src
├── __test__
│ └── inapp.test.js
├── inapp.js
├── index.css
├── index.js
└── qrcode.png
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react-native"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "env": {
5 | "browser": true,
6 | "node": true
7 | },
8 | "rules": {
9 | "no-console": 0,
10 | "no-useless-escape": 0,
11 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
12 | },
13 | "globals": {
14 | "jest": true,
15 | "describe": true,
16 | "it": true,
17 | "expect": true,
18 | "beforeAll": true,
19 | "afterAll": true,
20 | "beforeEach": true,
21 | "afterEach": true,
22 | "ga": true
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | .AppleDouble
3 | .LSOverride
4 |
5 | # Icon must end with two \r
6 | Icon
7 |
8 |
9 | # Thumbnails
10 | ._*
11 |
12 | # Files that might appear in the root of a volume
13 | .DocumentRevisions-V100
14 | .fseventsd
15 | .Spotlight-V100
16 | .TemporaryItems
17 | .Trashes
18 | .VolumeIcon.icns
19 | .com.apple.timemachine.donotpresent
20 |
21 | # Directories potentially created on remote AFP share
22 | .AppleDB
23 | .AppleDesktop
24 | Network Trash Folder
25 | Temporary Items
26 | .apdisk
27 |
28 | # Logs
29 | logs
30 | *.log
31 | npm-debug.log*
32 | yarn-debug.log*
33 | yarn-error.log*
34 |
35 | # Runtime data
36 | pids
37 | *.pid
38 | *.seed
39 | *.pid.lock
40 |
41 | # Directory for instrumented libs generated by jscoverage/JSCover
42 | lib-cov
43 |
44 | # Coverage directory used by tools like istanbul
45 | coverage
46 |
47 | # nyc test coverage
48 | .nyc_output
49 |
50 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
51 | .grunt
52 |
53 | # Bower dependency directory (https://bower.io/)
54 | bower_components
55 |
56 | # node-waf configuration
57 | .lock-wscript
58 |
59 | # Compiled binary addons (http://nodejs.org/api/addons.html)
60 | build/Release
61 |
62 | # Dependency directories
63 | node_modules/
64 | jspm_packages/
65 |
66 | # Typescript v1 declaration files
67 | typings/
68 |
69 | # Optional npm cache directory
70 | .npm
71 |
72 | # Optional eslint cache
73 | .eslintcache
74 |
75 | # Optional REPL history
76 | .node_repl_history
77 |
78 | # Output of 'npm pack'
79 | *.tgz
80 |
81 | # Yarn Integrity file
82 | .yarn-integrity
83 |
84 | # dotenv environment variables file
85 | .env
86 |
87 | /build
88 | /lib
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - '6.9'
5 |
6 | sudo: required
7 |
8 | after_success:
9 | - npm install -g codecov
10 | - codecov
11 |
12 | after_success:
13 | - yarn semantic-release
14 |
15 | branches:
16 | except:
17 | - /^v\d+\.\d+\.\d+$/
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Detect InApp
2 |
3 | detect browser or in-app information for mobile
4 |
5 | [](https://travis-ci.org/f2etw/detect-inapp)
6 | [](https://codecov.io/gh/f2etw/detect-inapp)
7 | [](https://npmjs.org/package/detect-inapp)
8 | [](https://npmjs.org/package/detect-inapp)
9 | [](http://standardjs.com)
10 | [](http://commitizen.github.io/cz-cli/)
11 | [](https://github.com/semantic-release/semantic-release)
12 |
13 | # Code Example
14 |
15 | ```
16 | import InApp from 'detect-inapp';
17 |
18 | const inapp = new InApp(navigator.userAgent || navigator.vendor || window.opera);
19 | ```
20 |
21 | # Installation
22 |
23 | `yarn add detect-inapp`
24 |
25 | # API Reference
26 |
27 | - `inapp.isMobile`
28 |
29 | - `inapp.isDesktop`
30 |
31 | - `inapp.isInApp`
32 |
33 | - `inapp.browser`
34 |
35 | `values: 'messenger', 'facebook', 'line', 'twitter', 'wechat', 'miui', 'instagram', 'chrome', 'safari', 'ie', 'firefox'`
36 |
37 | # License
38 |
39 | MIT License
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "detect-inapp",
3 | "version": "1.1.0",
4 | "description": "detect browser or in-app information for mobile",
5 | "main": "lib/inapp.js",
6 | "repository": "git@github.com:f2etw/detect-inapp.git",
7 | "author": "yuting1987@gmail.com",
8 | "license": "MIT",
9 | "scripts": {
10 | "prepublish": "npm run build",
11 | "develop": "jest --watch ./src",
12 | "test": "eslint ./src; jest --coverage ./src",
13 | "build": "rm -rf lib && mkdir lib && babel src/inapp.js --out-file lib/inapp.js",
14 | "demo": "react-scripts start",
15 | "deploy": "react-scripts build && gh-pages -d build",
16 | "semantic-release": "semantic-release pre && npm publish && semantic-release post"
17 | },
18 | "homepage": "https://f2etw.github.io/detect-inapp",
19 | "files": [
20 | "lib"
21 | ],
22 | "dependencies": {
23 | "lodash.findkey": "^4.6.0"
24 | },
25 | "devDependencies": {
26 | "babel-cli": "^6.24.1",
27 | "babel-core": "^6.24.1",
28 | "babel-eslint": "^7.2.3",
29 | "babel-jest": "^20.0.3",
30 | "babel-polyfill": "^6.23.0",
31 | "babel-preset-react-native": "^1.9.2",
32 | "clipboard": "^1.6.1",
33 | "cross-env": "^5.0.0",
34 | "eslint": "^3.19.0",
35 | "eslint-config-airbnb": "^15.0.1",
36 | "eslint-plugin-import": "^2.2.0",
37 | "eslint-plugin-jsx-a11y": "^5.0.3",
38 | "eslint-plugin-react": "^7.0.1",
39 | "gh-pages": "^1.0.0",
40 | "jest": "^20.0.3",
41 | "lodash": "^4.17.5",
42 | "primer-css": "^6.0.0",
43 | "react": "15.5.4",
44 | "react-dom": "15.5.4",
45 | "react-icons": "^2.2.5",
46 | "react-scripts": "1.0.1",
47 | "semantic-release": "^6.3.6"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Detect InApp
9 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/__test__/inapp.test.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import InApp from '../inapp';
3 |
4 | // check user agents from https://developers.whatismybrowser.com/
5 |
6 | const DESKTOP = {
7 | MACOS: {
8 | CHROME: [
9 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
10 | ],
11 | },
12 | WINDOWS: {
13 | CHROME: [
14 | 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
15 | ],
16 | FIREFOX: [
17 | 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0',
18 | ],
19 | IE11: [
20 | 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
21 | ],
22 | },
23 | UBUNTU: {
24 | CHROME: [
25 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36',
26 | ],
27 | FIREFOX: [
28 | 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0',
29 | ],
30 | VIVALDI: [
31 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.114 Safari/537.36 Vivaldi/1.9.818.50',
32 | ],
33 | },
34 | };
35 |
36 | const MOBILE = {
37 | IPHONE: {
38 | MESSENGER: [
39 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 [FBAN/MessengerForiOS;FBAV/117.0.0.36.70;FBBV/57539258;FBDV/iPhone7,2;FBMD/iPhone;FBSN/iOS;FBSV/10.2.1;FBSS/2;FBCR/中-華-電-信-;FBID/phone;FBLC/zh_TW;FBOP/5;FBRV/0]',
40 | ],
41 | FACEBOOK: [
42 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 [FBAN/FBIOS;FBAV/93.0.0.49.65;FBBV/58440824;FBDV/iPhone7,2;FBMD/iPhone;FBSN/iOS;FBSV/10.2.1;FBSS/2;FBCR/中-華-電-信-;FBID/phone;FBLC/zh_TW;FBOP/5;FBRV/0]',
43 | ],
44 | TWITTER: [
45 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Mobile/14D27 Twitter for iPhone',
46 | ],
47 | LINE: [
48 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 Safari Line/7.3.2',
49 | ],
50 | INSTAGRAM: [
51 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 Instagram 10.21.0 (iPhone7,2; iOS 10_2_1; zh-Hant_JP; zh-Hant-JP; scale=2.00; gamut=normal; 750x1334)',
52 | ],
53 | WECHAT: [
54 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 MicroMessenger/6.5.8 NetType/WIFI Language/zh_CN',
55 | ],
56 | CHROME: [
57 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/58.0.3029.113 Mobile/14D27 Safari/602.1',
58 | ],
59 | SAFARI: [
60 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Version/10.0 Mobile/14D27 Safari/602.1',
61 | ],
62 | },
63 | SONY: {
64 | MESSENGER: [
65 | 'Mozilla/5.0 (Linux; Android 6.0.1; D6653 Build/23.5.A.0.575; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 [FB_IAB/MESSENGER;FBAV/118.0.0.19.82;]',
66 | ],
67 | FACEBOOK: [
68 | 'Mozilla/5.0 (Linux; Android 6.0.1; D6653 Build/23.5.A.0.575; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/124.0.0.22.66;]',
69 | ],
70 | LINE: [
71 | 'Mozilla/5.0 (Linux; Android 6.0.1; D6653 Build/23.5.A.0.575; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 Line/7.4.0/IAB',
72 | ],
73 | INSTAGRAM: [
74 | 'Mozilla/5.0 (Linux; Android 6.0.1; D6653 Build/23.5.A.0.575; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 Instagram 10.21.0 Android (23/6.0.1; 480dpi; 1080x1776; Sony; D6653; D6653; qcom; zh_TW)',
75 | ],
76 | WECHAT: [
77 | 'Mozilla/5.0 (Linux; Android 6.0.1; D6653 Build/23.5.A.0.575; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 MicroMessenger/6.5.7.1041 NetType/WIFI Language/zh_TW',
78 | ],
79 | CHROME: [
80 | 'Mozilla/5.0 (Linux; Android 6.0.1; D6653 Build/23.5.A.0.575) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36',
81 | ],
82 | },
83 | PIXEL: {
84 | PUFFIN: [
85 | 'Mozilla/5.0 (Linux; Android 7.1.2; Pixel Build/N2G47E; zh-tw) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Puffin/6.0.9.15863AP',
86 | ],
87 | CHROME: [
88 | "Mozilla/5.0 (Linux; Android 12; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Mobile Safari/537.36",
89 | ],
90 | },
91 | HTC: {
92 | FACEBOOK: [],
93 | LINE: [],
94 | WECHAT: [],
95 | CHROME: [],
96 | },
97 | SAMSUNG: {
98 | MESSENGER: [
99 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N9208 Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 [FB_IAB/MESSENGER;FBAV/118.0.0.19.82;]',
100 | ],
101 | FACEBOOK: [
102 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N9208 Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/125.0.0.16.80;]',
103 | ],
104 | LINE: [
105 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N9208 Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 Line/7.4.0/IAB',
106 | ],
107 | WECHAT: [
108 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N9208 Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 MicroMessenger/6.5.7.1041 NetType/WIFI Language/zh_TW',
109 | ],
110 | INSTAGRAM: [
111 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N9208 Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 Instagram 10.22.0 Android (23/6.0.1; 560dpi; 1440x2560; samsung; SM-N9208; noblelte; samsungexynos7420; zh_TW)',
112 | ],
113 | CHROME: [
114 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N9208 Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36',
115 | ],
116 | },
117 | ASUS: {
118 | MESSENGER: [
119 | 'Mozilla/5.0 (Linux; Android 4.4.2; ASUS_T00F Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36 [FB_IAB/MESSENGER;FBAV/118.0.0.19.82;]',
120 | ],
121 | FACEBOOK: [
122 | 'Mozilla/5.0 (Linux; Android 4.4.2; ASUS_T00F Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36[FBAN/EMA;FBLC/zh_TW;FBAV/42.0.0.3.64;]',
123 | ],
124 | CHROME: [
125 | 'Mozilla/5.0 (Linux; Android 4.4.2; ASUS_T00F Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36',
126 | ],
127 | FIREFOX: [
128 | 'Mozilla/5.0 (Android 4.4.2; Mobile; rv:53.0) Gecko/53.0 Firefox/53.0',
129 | ],
130 | },
131 | REDMI: {
132 | MESSENGER: [
133 | 'Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 [FB_IAB/MESSENGER;FBAV/118.0.0.19.82;]',
134 | ],
135 | FACEBOOK: [
136 | 'Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/124.0.0.22.66;]',
137 | ],
138 | LINE: [
139 | 'Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 Line/7.4.0/IAB',
140 | ],
141 | INSTAGRAM: [
142 | 'Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36 Instagram 10.21.0 Android (23/6.0.1; 480dpi; 1080x1920; Xiaomi; Redmi Note 3; kate; qcom; zh_TW)',
143 | ],
144 | MIUI: [
145 | 'Mozilla/5.0 (Linux; U; Android 6.0.1; zh-tw; Redmi Note 3 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.5.8',
146 | ],
147 | CHROME: [
148 | 'Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36',
149 | ],
150 | },
151 | WINDOWS: {
152 | FACEBOOK: [],
153 | LINE: [],
154 | WECHAT: [],
155 | },
156 | };
157 |
158 | const TABLET = {
159 | IPAD: {
160 | MESSENGER: [
161 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304 [FBAN/MessengerForiOS;FBAV/117.0.0.36.70;FBBV/57539258;FBDV/iPad4,4;FBMD/iPad;FBSN/iOS;FBSV/10.3.1;FBSS/2;FBCR/;FBID/tablet;FBLC/zh_TW;FBOP/5;FBRV/0]',
162 | ],
163 | FACEBOOK: [
164 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304 [FBAN/FBIOS;FBAV/92.0.0.46.70;FBBV/57733420;FBDV/iPad4,4;FBMD/iPad;FBSN/iOS;FBSV/10.3.1;FBSS/2;FBCR/;FBID/tablet;FBLC/zh_TW;FBOP/5;FBRV/0]',
165 | ],
166 | TWITTER: [
167 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Mobile/14E304 Twitter for iPhone',
168 | ],
169 | LINE: [
170 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304 Safari Line/7.3.0',
171 | ],
172 | INSTAGRAM: [
173 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304 Instagram 10.20.0 (iPad4,4; iOS 10_3_1; zh_TW; zh-TW; scale=2.00; gamut=normal; 640x960)',
174 | ],
175 | WECHAT: [
176 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304 MicroMessenger/6.5.8 NetType/WIFI Language/zh_TW',
177 | ],
178 | CHROME: [
179 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/58.0.3029.83 Mobile/14E304 Safari/602.1',
180 | ],
181 | SAFARI: [
182 | 'Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
183 | ],
184 | },
185 | };
186 |
187 | describe('InApp', () => {
188 | describe('browser', () => {
189 | it('browser', () => {
190 | _.forEach(MOBILE, device => _.forEach(device, (useragents, name) =>
191 | _.forEach(useragents, (useragent) => {
192 | const inapp = new InApp(useragent);
193 | expect(inapp.browser).toBe(name.toLocaleLowerCase());
194 | }),
195 | ));
196 | });
197 | });
198 |
199 | describe('isMobile', () => {
200 | it('is mobile', () => {
201 | _.forEach(Object.assign({}, MOBILE, TABLET), device => _.forEach(device, useragents =>
202 | _.forEach(useragents, (useragent) => {
203 | const inapp = new InApp(useragent);
204 | expect(inapp.isMobile).toBe(true);
205 | }),
206 | ));
207 | });
208 |
209 | it('is not mobile', () => {
210 | _.forEach(DESKTOP, device => _.forEach(device, useragents =>
211 | _.forEach(useragents, (useragent) => {
212 | const inapp = new InApp(useragent);
213 | expect(inapp.isMobile).toBe(false);
214 | }),
215 | ));
216 | });
217 | });
218 |
219 | describe('isDesktop', () => {
220 | it('is desktop', () => {
221 | _.forEach(DESKTOP, device => _.forEach(device, useragents =>
222 | _.forEach(useragents, (useragent) => {
223 | const inapp = new InApp(useragent);
224 | expect(inapp.isDesktop).toBe(true);
225 | }),
226 | ));
227 | });
228 |
229 | it('is not desktop', () => {
230 | _.forEach(Object.assign({}, MOBILE, TABLET), device => _.forEach(device, useragents =>
231 | _.forEach(useragents, (useragent) => {
232 | const inapp = new InApp(useragent);
233 | expect(inapp.isDesktop).toBe(false);
234 | }),
235 | ));
236 | });
237 | });
238 |
239 | describe('inBot', () => {
240 | });
241 |
242 | describe('isInApp', () => {
243 | it('is in app', () => {
244 | _.forEach(Object.assign({}, MOBILE, TABLET), device => _.forEach(device, (useragents, name) =>
245 | _.forEach(useragents, (useragent) => {
246 | const inapp = new InApp(useragent);
247 | expect(inapp.isInApp).toBe(['CHROME', 'SAFARI', 'FIREFOX', 'MIUI', 'PUFFIN'].indexOf(name) < 0);
248 | }),
249 | ));
250 | });
251 |
252 | it('is not in app', () => {
253 | _.forEach(DESKTOP, device => _.forEach(device, useragents =>
254 | _.forEach(useragents, (useragent) => {
255 | const inapp = new InApp(useragent);
256 | expect(inapp.isInApp).toBe(false);
257 | }),
258 | ));
259 | });
260 | });
261 | });
262 |
--------------------------------------------------------------------------------
/src/inapp.js:
--------------------------------------------------------------------------------
1 | const findKey = require('lodash.findkey');
2 |
3 | const BROWSER = {
4 | messenger: /\bFB[\w_]+\/(Messenger|MESSENGER)/,
5 | facebook: /\bFB[\w_]+\//,
6 | twitter: /\bTwitter/i,
7 | line: /\bLine\//i,
8 | wechat: /\bMicroMessenger\//i,
9 | puffin: /\bPuffin/i,
10 | miui: /\bMiuiBrowser\//i,
11 | instagram: /\bInstagram/i,
12 | chrome: /\bCrMo\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?/,
13 | safari: /Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari/,
14 | ie: /IEMobile|MSIEMobile/,
15 | firefox: /fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS/,
16 | };
17 |
18 | class InApp {
19 |
20 | ua = '';
21 |
22 | constructor(useragent) {
23 | this.ua = useragent;
24 | }
25 |
26 | get browser(): string {
27 | return findKey(BROWSER, regex => regex.test(this.ua)) || 'other';
28 | }
29 |
30 | get isMobile(): boolean {
31 | return /(iPad|iPhone|Android|Mobile)/i.test(this.ua) || false;
32 | }
33 |
34 | get isDesktop(): boolean {
35 | return !this.isMobile;
36 | }
37 |
38 | get isInApp(): boolean {
39 | const rules = [
40 | 'WebView',
41 | '(iPhone|iPod|iPad)(?!.*Safari\/)',
42 | 'Android.*(wv)',
43 | ];
44 | const regex = new RegExp(`(${rules.join('|')})`, 'ig');
45 | return Boolean(this.ua.match(regex));
46 | }
47 | }
48 |
49 | module.exports = InApp;
50 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | }
4 |
5 | .container {
6 | width: 100%;
7 | max-width: 320px;
8 | }
9 |
10 | .p-3 {
11 | margin-top: 3px;
12 | }
13 |
14 | .qrcode {
15 | text-align: center;
16 | }
17 | textarea {
18 | font-family: monospace, monospace;
19 | }
20 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
2 |
3 | import 'primer-css/build/build.css';
4 | import React, { Component } from 'react';
5 | import ReactDOM from 'react-dom';
6 | import Clippy from 'react-icons/lib/go/clippy';
7 | import GitHub from 'react-icons/lib/go/mark-github';
8 | import DiffRenamed from 'react-icons/lib/go/diff-renamed';
9 | import Clipboard from 'clipboard';
10 | import InApp from './inapp';
11 | import qrcode from './qrcode.png';
12 | import './index.css';
13 |
14 | class App extends Component {
15 |
16 | state = {
17 | inapp: null,
18 | value: '',
19 | uri: 'twitter://',
20 | }
21 |
22 | componentWillMount() {
23 | const useragent = navigator.userAgent || navigator.vendor || window.opera;
24 | const inapp = new InApp(useragent);
25 | const value = [`${useragent}`];
26 | if (navigator) for (let key in navigator) value.push(`${key}=${navigator[key]}`); // eslint-disable-line
27 | this.setState({ inapp, value: value.join('\n') });
28 | window.ga('send', 'event', 'useragent', useragent, inapp.browser);
29 | }
30 |
31 | componentDidMount() {
32 | new Clipboard('.copy'); // eslint-disable-line
33 | }
34 |
35 | onUriChange = e => this.setState({ uri: e.target.value });
36 |
37 | onOpenClick = async () => {
38 | const { inapp, uri } = this.state;
39 | const reply = await inapp.open({ ios: { uri } });
40 | if (!reply) alert('Cannot Open'); // eslint-disable-line
41 | }
42 |
43 | render() {
44 | const { inapp, value, uri } = this.state;
45 |
46 | return (
47 |
48 |
63 |
64 |
65 |
66 | {inapp.browser}
67 |
inapp.browser
68 |
69 |
70 | {inapp.isMobile ? 'true' : 'false'}
71 |
inapp.isMobile()
72 |
73 |
74 | {inapp.isDesktop ? 'true' : 'false'}
75 |
inapp.isDesktop()
76 |
77 |
78 | {inapp.isInApp ? 'true' : 'false'}
79 |
inapp.isInApp()
80 |
81 |
82 |
83 |
84 |
85 |
88 |
89 |
90 |
inapp.open()
91 |
92 |
93 |
94 |
95 |
96 |

97 |
98 |
103 |
104 |
105 | );
106 | }
107 | }
108 |
109 | ReactDOM.render(, document.getElementById('root'));
110 |
--------------------------------------------------------------------------------
/src/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f2etw/detect-inapp/dade9eec113bf8b0a9ea7093c2085e1defd9ddb4/src/qrcode.png
--------------------------------------------------------------------------------