├── .eslintrc.json
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmrc
├── .prettierrc.json
├── LICENSE
├── README.md
├── ionic-react-hooks-gh.png
├── ionic-react-hooks.png
├── jest.setup.js
├── lerna.json
├── package-lock.json
├── package.json
├── packages
├── app
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useApp.test.ts
│ │ ├── useApp.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── browser
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useBrowser.test.ts
│ │ ├── useBrowser.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── camera
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useCamera.test.ts
│ │ ├── useCamera.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── clipboard
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useClipboard.test.ts
│ │ ├── useClipboard.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── device
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useDevice.test.ts
│ │ ├── useDevice.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── filesystem
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useFileSystem.ts
│ │ ├── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ │ └── utils.ts
│ └── tsconfig.json
├── geolocation
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useGeolocation.test.ts
│ │ ├── useGeolocation.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── keyboard
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useKeyboard.test.ts
│ │ ├── useKeyboard.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── network
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useNetwork.test.ts
│ │ ├── useNetwork.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── screen-reader
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useScreenReader.test.ts
│ │ ├── useScreenReader.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
├── storage
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── useStorage.test.ts
│ │ ├── useStorage.ts
│ │ └── util
│ │ │ ├── feature-check.ts
│ │ │ └── models.ts
│ └── tsconfig.json
└── test-app
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── capacitor.config.json
│ ├── ionic.config.json
│ ├── ios
│ ├── .gitignore
│ └── App
│ │ ├── App.xcodeproj
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ ├── App.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── App
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── AppIcon-20x20@1x.png
│ │ │ │ ├── AppIcon-20x20@2x-1.png
│ │ │ │ ├── AppIcon-20x20@2x.png
│ │ │ │ ├── AppIcon-20x20@3x.png
│ │ │ │ ├── AppIcon-29x29@1x.png
│ │ │ │ ├── AppIcon-29x29@2x-1.png
│ │ │ │ ├── AppIcon-29x29@2x.png
│ │ │ │ ├── AppIcon-29x29@3x.png
│ │ │ │ ├── AppIcon-40x40@1x.png
│ │ │ │ ├── AppIcon-40x40@2x-1.png
│ │ │ │ ├── AppIcon-40x40@2x.png
│ │ │ │ ├── AppIcon-40x40@3x.png
│ │ │ │ ├── AppIcon-512@2x.png
│ │ │ │ ├── AppIcon-60x60@2x.png
│ │ │ │ ├── AppIcon-60x60@3x.png
│ │ │ │ ├── AppIcon-76x76@1x.png
│ │ │ │ ├── AppIcon-76x76@2x.png
│ │ │ │ ├── AppIcon-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── Splash.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── splash-2732x2732-1.png
│ │ │ │ ├── splash-2732x2732-2.png
│ │ │ │ └── splash-2732x2732.png
│ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── capacitor.config.json
│ │ └── config.xml
│ │ └── Podfile
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── assets
│ │ ├── icon
│ │ │ └── favicon.png
│ │ └── shapes.svg
│ ├── favicon.ico
│ └── index.html
│ ├── scripts
│ ├── build.js
│ ├── start.js
│ └── test.js
│ ├── src
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ └── Menu.tsx
│ ├── declarations.ts
│ ├── index.tsx
│ ├── pages
│ │ ├── AppPage.tsx
│ │ ├── BrowserPage.tsx
│ │ ├── CameraPage.tsx
│ │ ├── ClipboardPage.tsx
│ │ ├── DevicePage.tsx
│ │ ├── GeolocationPage.tsx
│ │ ├── Home.css
│ │ ├── Home.tsx
│ │ ├── KeyboardPage.tsx
│ │ ├── NetworkPage.tsx
│ │ ├── ScreenReaderPage.tsx
│ │ ├── StoragePage.tsx
│ │ └── template.tsx
│ ├── react-app-env.d.ts
│ ├── theme.css
│ └── theme
│ │ └── variables.css
│ ├── tsconfig.json
│ └── tslint.json
├── tsconfig.json
└── tslint.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "plugins": ["@typescript-eslint", "prettier"],
4 | "extends": [
5 | "eslint:recommended",
6 | "plugin:@typescript-eslint/recommended",
7 | "prettier"
8 | ],
9 | "rules": {
10 | "no-fallthrough": "off",
11 | "no-constant-condition": "off",
12 | "@typescript-eslint/no-this-alias": "off",
13 | "@typescript-eslint/no-explicit-any": "off",
14 | "@typescript-eslint/explicit-module-boundary-types": [
15 | "warn",
16 | { "allowArgumentsExplicitlyTypedAsAny": true }
17 | ],
18 | "prettier/prettier": "error"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 | pull_request:
8 | branches:
9 | - '**'
10 |
11 | jobs:
12 | verify:
13 | runs-on: ubuntu-latest
14 | timeout-minutes: 30
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v2
18 | with:
19 | node-version: 14.x
20 | cache: 'npm'
21 | cache-dependency-path: '**/package.json'
22 | - run: npm ci
23 | - run: npx lerna run lint
24 | - run: npx lerna run build
25 | deploy:
26 | runs-on: ubuntu-latest
27 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release):')
28 | timeout-minutes: 30
29 | needs:
30 | - verify
31 | steps:
32 | - uses: actions/checkout@v2
33 | with:
34 | fetch-depth: 0
35 | - uses: actions/setup-node@v2
36 | with:
37 | node-version: 14.x
38 | cache: 'npm'
39 | cache-dependency-path: '**/package.json'
40 | - name: Setup npm auth
41 | run: |
42 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
43 | npm whoami
44 | env:
45 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
46 | - run: npm ci
47 | - run: npx lerna run build
48 | - run: npm run lerna:publish || true
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | build
13 | dist
14 | types
15 | *.tgz
16 |
17 | # misc
18 | .DS_Store
19 | .env.local
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 |
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=true
2 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": true,
3 | "printWidth": 100,
4 | "semi": true,
5 | "singleQuote": true,
6 | "jsxSingleQuote": false,
7 | "tabWidth": 2,
8 | "trailingComma": "es5",
9 |
10 | "requirePragma": false
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015-present Drifty Co.
2 | http://drifty.com/
3 |
4 | MIT License
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Capacitor React Hooks
3 |
4 |
5 | A set of hooks to help Capacitor developers use native Capacitor APIs .
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ## Maintainers
21 |
22 | | Maintainer | GitHub | Social |
23 | | ------------ | --------------------------------------------- | ----------------------------------------------- |
24 | | Ely Lucas | [elylucas](https://github.com/elylucas) | [@elylucas](https://twitter.com/elylucas) |
25 |
26 | > These docs are for Capacitor 3 plugins. For docs that target v2 plugins, see the [capv2](https://github.com/capacitor-community/react-hooks/tree/capv2) branch.
27 | ## Getting Started
28 |
29 | To start using Capacitor Hooks in your app, you install the React Hook package along with the Capacitor plugin you want to use. Here is an example of using the Storage plugin along with it's React hook:
30 |
31 | ```bash
32 | # Install the Capacitor Plugin
33 | npm install @capacitor/storage
34 | # And then the React hook package:
35 | npm install @capacitor-community/storage-react
36 | ```
37 |
38 | Import the hooks:
39 | ```jsx
40 | import { useStorage } from '@capacitor-community/storage-react'
41 | ```
42 |
43 | Then use the hooks in your app:
44 |
45 | ```jsx
46 | const [value, setValue] = useStorage('mykey');
47 | ```
48 |
49 | ## Feature Detection
50 |
51 | While Capacitor allows you to write to one API across several platforms, not all features are supported on all platforms. It is encouraged to check if the feature you intend to use is available before using it to avoid any runtime errors.
52 |
53 | Each of the hook plugin paths exports an `availableFeatures` object, which contains a list features for that plugin. If the feature is supported for the current platform the app is running on, that feature will be true:
54 |
55 | ```jsx
56 | const { useStorageItem, availableFeatures } = `@capacitor-community/storage-react`;
57 | const [value, setValue] = useStorage('mykey');
58 | ...
59 | if(availableFeatures.useStorage) {
60 | // Storage is available, feel free to use it!
61 | setValue('cake');
62 | }
63 | ```
64 |
65 | # Upgrading from Capacitor 2 React Hooks
66 |
67 | In Capacitor 3, all the plugins were separated into their own packages. Likewise, the new React hooks plugins were also put into their own package, so you will need to install the hook for each plugin you use.
68 |
69 | Any deprecated API'S from Capacitor 2 to 3 were also removed and updated, so you might need to make some modifications to account for API changes. See the [Capacitor Plugin API for](https://capacitorjs.com/docs/plugins) to learn more.
70 |
71 | # Hook Usage
72 |
73 | ## @capacitor/app Hooks
74 |
75 | Installation:
76 |
77 | ```bash
78 | npm install @capacitor-community/app-react
79 | ```
80 |
81 | Usage:
82 |
83 | ```jsx
84 | import { useAppState, useAppUrlOpen, useLaunchUrl, availableFeatures } from '@capacitor-community/app-react';
85 | ```
86 |
87 | `useAppState` provides access to App status information, such as whether the app is active or inactive. This value will update dynamically.
88 |
89 | ```jsx
90 | const {state} = useAppState();
91 | ```
92 |
93 | #### `useLaunchUrl`
94 |
95 | `useLaunchUrl` provides the URL the app was initially launched with. If you'd like to track future inbound URL events, use `useAppUrlOpen` below instead.
96 |
97 | ```jsx
98 | const { launchUrl } = useLaunchUrl();
99 | ```
100 |
101 | #### `useAppUrlOpen`
102 |
103 | `useAppUrlOpen` provides the most recent URL used to activate the app. For example, if the user followed a link in another app that opened your app.
104 |
105 | ```jsx
106 | const { appUrlOpen } = useAppUrlOpen();
107 | ```
108 |
109 | See the [App](https://capacitorjs.com/docs/apis/app) Capacitor Plugin docs for more info on the plugin API.
110 |
111 | ## @capcitor/browser Hooks
112 |
113 | Installation:
114 |
115 | ```bash
116 | npm install @capacitor-community/browser-react
117 | ```
118 |
119 | Usage:
120 |
121 | ```jsx
122 | import { useClose, useOpen, availableFeatures } from '@capacitor-community/browser-react';
123 | ```
124 |
125 | `useOpen`, `useClose` provides a way to launch, and close an in-app browser for external content:
126 |
127 | ```jsx
128 | // Open url in browser
129 | const { open } = useOpen();
130 |
131 | open({ url: 'http://ionicframework.com' });
132 |
133 | // Close url in browser
134 | const { close } = useClose();
135 | useClose();
136 | ```
137 |
138 | See the [Browser](https://capacitorjs.com/docs/apis/browser) Capacitor Plugin docs for more info on the plugin API.
139 |
140 | ## @capacitor/camera Hooks
141 |
142 | Installation:
143 |
144 | ```bash
145 | npm install @capacitor-community/camera-react
146 | ```
147 |
148 | Usage:
149 |
150 | ```jsx
151 | import { useCamera, availableFeatures } from '@capacitor-community/camera-react';
152 | ```
153 |
154 | `useCamera` provides a way to take a photo:
155 |
156 | ```jsx
157 | const { photo, getPhoto } = useCamera();
158 | const triggerCamera = useCallback(async () => {
159 | getPhoto({
160 | quality: 100,
161 | allowEditing: false,
162 | resultType: CameraResultType.DataUrl
163 | })
164 | }, [getPhoto]);
165 |
166 | {photo &&
}
167 | ```
168 |
169 | See the [Camera](https://capacitorjs.com/docs/apis/camera) Capacitor Plugin docs for more info on the plugin API.
170 |
171 | ## Clipboard Hooks
172 |
173 | Installation:
174 |
175 | ```bash
176 | npm install @capacitor-community/clipboard-react
177 | ```
178 |
179 | Usage:
180 |
181 | ```jsx
182 | import { useClipboard, availableFeatures } from '@capacitor-community/clipboard-react';
183 | ```
184 |
185 | `useClipboard` reads and writes clipboard data:
186 |
187 | ```jsx
188 | const { value, getValue, setValue } = useClipboard();
189 |
190 | const paste = useCallback(async () => {
191 | await setValue('http://ionicframework.com/);
192 | }, [setValue]);
193 |
194 | const copy = useCallback(async () => {
195 | getValue();
196 | }, [getValue])
197 | ```
198 |
199 | See the [Clipboard](https://capacitorjs.com/docs/apis/clipboard) Capacitor Plugin docs for more info on the plugin API.
200 |
201 | ## Device Hooks
202 |
203 | Installation:
204 |
205 | ```bash
206 | npm install @capacitor-community/device-react
207 | ```
208 |
209 | Usage:
210 |
211 | ```jsx
212 | import { useGetInfo, useGetLanguageCode, availableFeatures } from '@capacitor-community/device-react';
213 | ```
214 |
215 | `useGetInfo`, `useGetLanguageCode` gives access to device information and device language settings:
216 |
217 | ```jsx
218 | const { info } = useGetInfo();
219 | const { languageCode } = useGetLanguageCode();
220 | ```
221 |
222 | See the [Device](https://capacitorjs.com/docs/apis/device) Capacitor Plugin docs for more info on the plugin API.
223 |
224 | ## Filesystem Hooks
225 |
226 | Installation:
227 |
228 | ```bash
229 | npm install @capacitor-community/filesystem-react
230 | ```
231 |
232 | Usage:
233 |
234 | ```jsx
235 | import { useFilesystem, base64FromPath, availableFeatures } from '@capacitor-community/filesystem-react';
236 | ```
237 |
238 | `useFilesystem` returns back common methods to gain access to file system apis.
239 |
240 | ```jsx
241 | const { readFile } = useFilesystem();
242 |
243 | const file = await readFile({
244 | path: filepath,
245 | directory: FilesystemDirectory.Data
246 | });
247 | ```
248 |
249 | `base64FromPath` is a helper method that will take in a path to a file and return back the base64 encoded representation of that file.
250 |
251 | See the [Filesystem](https://capacitorjs.com/docs/apis/filesystem) Capacitor Plugin docs for more info on the plugin API.
252 |
253 | ```jsx
254 | const base64String = await base64FromPath(path);
255 | ```
256 |
257 | ## Geolocation Hooks
258 |
259 | Installation:
260 |
261 | ```bash
262 | npm install @capacitor-community/geolocation-react
263 | ```
264 |
265 | Usage:
266 |
267 | ```jsx
268 | import { useCurrentPosition, useWatchPosition, availableFeatures } from '@capacitor-community/geolocation-react';
269 | ```
270 |
271 | `useCurrentPosition` returns a single geolocation position using the Geolocation API in Capacitor. The position can be manually updated by calling `getPosition`:
272 |
273 | ```jsx
274 | const { currentPosition, getPosition } = useCurrentPosition();
275 |
276 | const handleRefreshPosition = () => {
277 | getPosition();
278 | }
279 | ```
280 |
281 | `useWatchPosition` tracks a geolocation position using the `watchPosition` in the Geolocation API in Capacitor. The location will automatically begin updating, and you can use the `clearWatch` and `startWatch` methods to manually stop and restart the watch.
282 |
283 | ```jsx
284 | const { currentPosition, startWatch, clearWatch } = useWatchPosition();
285 | ```
286 |
287 | See the [Geolocation](https://capacitorjs.com/docs/apis/geolocation) Capacitor Plugin docs for more info on the plugin API.
288 | ## Keyboard Hooks
289 |
290 | Installation:
291 |
292 | ```bash
293 | npm install @capacitor-community/keyboard-react
294 | ```
295 |
296 | Usage:
297 |
298 | ```jsx
299 | import { useKeyboardState } from '@capacitor-community/keyboard';
300 | ```
301 |
302 | `useKeyboard` returns whether or not the on-screen keyboard is shown as well as an approximation of the keyboard height in pixels.
303 |
304 | ```jsx
305 | const { isOpen, keyboardHeight } = useKeyboard();
306 | // Use keyboardHeight to translate an input that would otherwise be hidden by the keyboard
307 | ```
308 |
309 | See the [Keyboard](https://capacitorjs.com/docs/apis/keyboard) Capacitor Plugin docs for more info on the plugin API.
310 |
311 | ## Network Hooks
312 |
313 | Installation:
314 |
315 | ```bash
316 | npm install @capacitor-community/network-react
317 | ```
318 |
319 | Usage:
320 |
321 | ```jsx
322 | import { useStatus, availableFeatures } from '@capacitor-community/network-react';
323 | ```
324 |
325 | `useStatus` monitors network status and information:
326 |
327 | ```jsx
328 | const { networkStatus } = useStatus();
329 | ```
330 |
331 | See the [Network](https://capacitorjs.com/docs/apis/network) Capacitor Plugin docs for more info on the plugin API.
332 | ## ScreenReader Hooks
333 |
334 | Installation:
335 |
336 | ```bash
337 | npm install @capacitor-community/screen-reader-react
338 | ```
339 |
340 | Usage:
341 |
342 | ```jsx
343 | import { useIsScreenReaderEnabled, useSpeak, availableFeatures } from '@capacitor-community/screen-reader-react';
344 | ```
345 |
346 | `useIsScreenReaderEnabled` provides access to detecting and responding to a screen reading device or OS setting being enabled:
347 |
348 | ```jsx
349 | const {isScreenReaderEnabled} = useIsScreenReaderEnabled();
350 | ```
351 |
352 | `useSpeak` activates a text-to-speech engine (if available) to read spoken text.
353 | ```jsx
354 | const { speak } = useSpeak();
355 | speak({value: textToSpeak})
356 | ```
357 |
358 | See the [ScreenReader](https://capacitorjs.com/docs/apis/screenreader) Capacitor Plugin docs for more info on the plugin API.
359 |
360 | ## Storage Hooks
361 |
362 | Installation:
363 |
364 | ```bash
365 | npm install @capacitor-community/storage-react
366 | ```
367 |
368 | Usage:
369 |
370 | ```jsx
371 | import { useStorage, useStorageItem, availableFeatures } from '@capacitor-community/storage-react';
372 | ```
373 |
374 | `useStorage` provides access to Capacitor's storage engine. There is also a helper called `useStorageItem` which makes managing a single item easy if you don't need to access the full Storage API (see below)
375 |
376 | ```jsx
377 | const { get, set, remove, getKeys, clear } = useStorage();
378 | useEffect(() => {
379 | async function example() {
380 | const value = await get('name');
381 | await set('name', 'Max');
382 | await remove('name');
383 | const allKeys = await getKeys();
384 | await clear();
385 | }
386 | }, [ get, set, remove, keys, clear ]);
387 | ```
388 |
389 | #### `useStorageItem`
390 |
391 | `useStorageItem` tracks a single item and provides a nice way to read and write that item:
392 |
393 | ```jsx
394 | const [ name , setName ] = useStorageItem('name', 'Max');
395 |
396 | // Example:
397 | const updateName = useCallback((n) => {
398 | setName(n);
399 | }, [ setName ]);
400 | ```
401 |
402 | `useStorageItem` will use the initial value already in storage, or the one provided if there is no existing value.
403 |
404 | See the [Storage](https://capacitorjs.com/docs/apis/storage) Capacitor Plugin docs for more info on the plugin API.
405 |
406 | # Other Hooks
407 |
408 | This is an evolving project and not all of the Capacitor Plugins are supported yet. If there is one you need, feel free top open an issue for it, or better yet, submit a PR!
409 |
--------------------------------------------------------------------------------
/ionic-react-hooks-gh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/ionic-react-hooks-gh.png
--------------------------------------------------------------------------------
/ionic-react-hooks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/ionic-react-hooks.png
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/jest.setup.js
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": ["packages/*"],
3 | "version": "independent",
4 | "command": {
5 | "bootstrap": {
6 | "hoist": true,
7 | "ci": true
8 | },
9 | "version": {
10 | "allowBranch": "main",
11 | "conventionalCommits": true,
12 | "createRelease": "github",
13 | "message": "chore(release): Release",
14 | "tagVersionPrefix": ""
15 | },
16 | "publish": {
17 | "verifyAccess": false
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "capacitor-react-hooks",
3 | "private": true,
4 | "description": "React Hooks for Capacitor apps",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "scripts": {
18 | "lerna:publish": "lerna publish from-git --dist-tag latest --yes",
19 | "lerna:version": "lerna version --conventional-commits",
20 | "prerelease": "lerna run build && lerna run lint",
21 | "release": "npm run lerna:version"
22 | },
23 | "devDependencies": {
24 | "@capacitor/browser": "^1.0.4",
25 | "@capacitor/camera": "^1.1.1",
26 | "@capacitor/cli": "^3.2.4",
27 | "@capacitor/clipboard": "^1.0.4",
28 | "@capacitor/core": "^3.2.4",
29 | "@capacitor/device": "^1.0.4",
30 | "@capacitor/filesystem": "^1.0.4",
31 | "@capacitor/geolocation": "^1.1.0",
32 | "@capacitor/keyboard": "^1.1.0",
33 | "@capacitor/network": "^1.0.3",
34 | "@capacitor/screen-reader": "1.0.3",
35 | "@capacitor/storage": "^1.2.0",
36 | "@testing-library/react-hooks": "^7.0.2",
37 | "@types/jest": "^27.0.2",
38 | "@types/react": "^17.0.29",
39 | "@typescript-eslint/eslint-plugin": "^5.0.0",
40 | "@typescript-eslint/parser": "^5.0.0",
41 | "eslint": "^8.0.0",
42 | "eslint-config-prettier": "^8.3.0",
43 | "eslint-plugin-prettier": "^4.0.0",
44 | "jest": "^27.2.5",
45 | "lerna": "^4.0.0",
46 | "prettier": "^2.4.1",
47 | "react": "^17.0.2",
48 | "react-test-renderer": "^17.0.2",
49 | "rimraf": "^2.6.1",
50 | "rollup": "^2.58.0",
51 | "ts-jest": "^27.0.5",
52 | "typescript": "^4.4.4"
53 | },
54 | "dependencies": {
55 | "@capacitor-community/app-react": "file:packages/app",
56 | "@capacitor-community/browser-react": "file:packages/browser",
57 | "@capacitor-community/camera-react": "file:packages/camera",
58 | "@capacitor-community/clipboard-react": "file:packages/clipboard",
59 | "@capacitor-community/device-react": "file:packages/device",
60 | "@capacitor-community/filesystem-react": "file:packages/filesystem",
61 | "@capacitor-community/geolocation-react": "file:packages/geolocation",
62 | "@capacitor-community/keyboard-react": "file:packages/keyboard",
63 | "@capacitor-community/network-react": "file:packages/network",
64 | "@capacitor-community/screen-reader-react": "file:packages/screen-reader",
65 | "@capacitor-community/storage-react": "file:packages/storage",
66 | "react-ionic-hooks-test-app": "file:packages/test-app"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/packages/app/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/app-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/app-react@0.0.2...@capacitor-community/app-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/app-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/app-react@0.0.1...@capacitor-community/app-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/app-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/app-react
33 |
--------------------------------------------------------------------------------
/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/app-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor App Plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lerna:release": "",
26 | "lint": "npm run eslint",
27 | "lint.fix": "npm run eslint -- --fix",
28 | "test": "echo tests are in process",
29 | "transpile": "tsc"
30 | },
31 | "peerDependencies": {
32 | "@capacitor/app": "*",
33 | "@capacitor/core": ">=3.0.0",
34 | "react": "*"
35 | },
36 | "prettier": "../../.prettierrc.json",
37 | "eslintConfig": {
38 | "extends": "../../.eslintrc.json"
39 | },
40 | "jest": {
41 | "preset": "ts-jest",
42 | "testPathIgnorePatterns": [
43 | "node_modules",
44 | "dist-transpiled",
45 | "dist",
46 | "test-app"
47 | ],
48 | "modulePaths": [
49 | ""
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/packages/app/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useApp';
2 |
--------------------------------------------------------------------------------
/packages/app/src/useApp.test.ts:
--------------------------------------------------------------------------------
1 | jest.mock('@capacitor/core', () => {
2 | let appStateListener: any;
3 | let appUrlOpenListener: any;
4 | const isActive = true;
5 | return {
6 | Plugins: {
7 | App: {
8 | addListener: (eventName: string, cb: any) => {
9 | switch (eventName) {
10 | case 'appStateChange':
11 | appStateListener = cb;
12 | break;
13 | case 'appUrlOpen':
14 | appUrlOpenListener = cb;
15 | break;
16 | }
17 | return {
18 | remove: () => {
19 | return;
20 | },
21 | };
22 | },
23 | __updateAppState: () => {
24 | appStateListener && appStateListener({ isActive: !isActive });
25 | },
26 | __updateAppUrlOpen: () => {
27 | appUrlOpenListener && appUrlOpenListener({ url: 'my-app://very-legal-very-cool' });
28 | },
29 | getLaunchUrl: async () => {
30 | return { url: 'my-app://awesome' };
31 | },
32 | },
33 | },
34 | Capacitor: {
35 | isPluginAvailable: () => true,
36 | platform: 'ios',
37 | },
38 | };
39 | });
40 |
41 | import { useAppState, useAppUrlOpen, useLaunchUrl } from './useApp';
42 | import { act, renderHook } from '@testing-library/react-hooks';
43 | import { Plugins } from '@capacitor/core';
44 |
45 | it('Gets app launch URL', async () => {
46 | const r = renderHook(() => useLaunchUrl());
47 | await act(async () => {
48 | const result = r.result;
49 | const { isAvailable } = result.current;
50 | expect(isAvailable).toBe(true);
51 | await r.waitForNextUpdate();
52 | expect(r.result.current.launchUrl).toBe('my-app://awesome');
53 | });
54 | });
55 |
56 | it('Gets app open URL', async () => {
57 | const r = renderHook(() => useAppUrlOpen());
58 |
59 | await act(async () => {
60 | const { appUrlOpen } = r.result.current;
61 | expect(appUrlOpen).toBeUndefined();
62 |
63 | (Plugins.App as any).__updateAppUrlOpen();
64 | });
65 |
66 | await act(async () => {
67 | const { appUrlOpen } = r.result.current;
68 | expect(appUrlOpen).toBe('my-app://very-legal-very-cool');
69 | });
70 | });
71 |
72 | it('Gets app state', async () => {
73 | const r = renderHook(() => useAppState());
74 | await act(async () => {
75 | const result = r.result;
76 |
77 | expect(result.current.state).toBe(true);
78 |
79 | (Plugins.App as any).__updateAppState();
80 | });
81 |
82 | await act(async () => {
83 | const state = r.result.current;
84 | expect(state.state).toBe(false);
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/packages/app/src/useApp.ts:
--------------------------------------------------------------------------------
1 | import { App, AppState, URLOpenListenerEvent } from '@capacitor/app';
2 | import { Capacitor } from '@capacitor/core';
3 | import { AvailableResult, notAvailable } from './util/models';
4 |
5 | import { isFeatureAvailable } from './util/feature-check';
6 | import { useEffect, useState } from 'react';
7 |
8 | interface AppUrlOpenResult extends AvailableResult {
9 | appUrlOpen?: string;
10 | }
11 | interface AppStateResult extends AvailableResult {
12 | state?: boolean;
13 | }
14 | interface LaunchUrlResult extends AvailableResult {
15 | launchUrl?: string;
16 | }
17 |
18 | export const availableFeatures = {
19 | appState: isFeatureAvailable('App', 'state'),
20 | getLaunchUrl: isFeatureAvailable('App', 'getLaunchUrl'),
21 | appUrlOpen: isFeatureAvailable('App', 'appUrlOpen'),
22 | };
23 |
24 | if (!Capacitor.isPluginAvailable('App')) {
25 | console.warn('The @capacitor/app plugin was not found, did you forget to install it?');
26 | }
27 |
28 | export function useAppState(): AppStateResult {
29 | if (!availableFeatures.appState) {
30 | return notAvailable;
31 | }
32 |
33 | const [state, setAppState] = useState(true);
34 |
35 | useEffect(() => {
36 | const listener = App.addListener('appStateChange', async (state: AppState) => {
37 | setAppState(state.isActive);
38 | });
39 |
40 | return () => {
41 | listener.remove();
42 | };
43 | }, [App, setAppState]);
44 |
45 | return {
46 | state,
47 | isAvailable: true,
48 | };
49 | }
50 |
51 | /**
52 | * Get the URL the app was originally launched with. Note: if
53 | * you want to detect future app opens, use `useAppUrlOpen` instead,
54 | * which will stay updated.
55 | */
56 | export function useLaunchUrl(): LaunchUrlResult {
57 | if (!availableFeatures.getLaunchUrl) {
58 | return notAvailable;
59 | }
60 |
61 | const [launchUrl, setUrl] = useState();
62 |
63 | useEffect(() => {
64 | async function getAppLaunchUrl() {
65 | const ret = await App.getLaunchUrl();
66 | setUrl(ret?.url);
67 | }
68 | getAppLaunchUrl();
69 | }, [App, setUrl]);
70 |
71 | return {
72 | launchUrl,
73 | isAvailable: true,
74 | };
75 | }
76 |
77 | export function useAppUrlOpen(): AppUrlOpenResult {
78 | if (!isFeatureAvailable('App', 'appUrlOpen')) {
79 | return notAvailable;
80 | }
81 |
82 | const [appUrlOpen, setAppUrl] = useState();
83 |
84 | useEffect(() => {
85 | const listener = App.addListener('appUrlOpen', async (state: URLOpenListenerEvent) => {
86 | setAppUrl(state.url);
87 | });
88 | return () => {
89 | listener.remove();
90 | };
91 | }, [App, setAppUrl]);
92 |
93 | return {
94 | appUrlOpen,
95 | isAvailable: true,
96 | };
97 | }
98 |
--------------------------------------------------------------------------------
/packages/app/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | App: {
13 | state: allTrue,
14 | getLaunchUrl: { ...allTrue, web: false },
15 | appUrlOpen: { ...allTrue, web: false },
16 | },
17 | };
18 |
19 | export function isFeatureAvailable<
20 | T extends typeof featureMap,
21 | PluginKeys extends keyof NonNullable,
22 | FeatureKeys extends keyof NonNullable[PluginKeys]>
23 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
24 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
25 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
26 | if (isPluginAvailable && !!isFeatureSupported) {
27 | return true;
28 | }
29 | return false;
30 | }
31 |
32 | export function featureNotAvailableError(): any {
33 | throw new FeatureNotAvailableError();
34 | }
35 |
--------------------------------------------------------------------------------
/packages/app/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/browser/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/browser-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/browser-react@0.0.2...@capacitor-community/browser-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/browser-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/browser-react@0.0.1...@capacitor-community/browser-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/browser-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/browser-react
33 |
--------------------------------------------------------------------------------
/packages/browser/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/browser-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Browser plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/browser": "*",
32 | "@capacitor/core": ">=3.0.0",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/browser/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useBrowser';
2 |
--------------------------------------------------------------------------------
/packages/browser/src/useBrowser.test.ts:
--------------------------------------------------------------------------------
1 | const mock = {
2 | Plugins: {
3 | Browser: {
4 | text: 'fake',
5 | open: jest.fn(),
6 | prefetch: jest.fn(),
7 | close: jest.fn(),
8 | },
9 | },
10 | Capacitor: {
11 | isPluginAvailable: () => true,
12 | platform: 'ios',
13 | },
14 | };
15 |
16 | jest.mock('@capacitor/core', () => mock);
17 |
18 | import { useOpen, useClose } from './useBrowser';
19 | import { renderHook, act } from '@testing-library/react-hooks';
20 |
21 | it('Opens url', async () => {
22 | const { result } = renderHook(() => useOpen());
23 |
24 | await act(async () => {
25 | const { open, isAvailable } = result.current;
26 | expect(isAvailable).toBe(true);
27 | await open({ url: 'http://ionicframework.com' });
28 | expect(mock.Plugins.Browser.open).toHaveBeenCalledWith({ url: 'http://ionicframework.com' });
29 | });
30 | jest.resetAllMocks();
31 | });
32 |
33 | it('Closes url', async () => {
34 | const { result } = renderHook(() => useClose());
35 |
36 | await act(async () => {
37 | const { close, isAvailable } = result.current;
38 | expect(isAvailable).toBe(true);
39 | await close();
40 | expect(mock.Plugins.Browser.close).toHaveBeenCalledTimes(1);
41 | });
42 | jest.resetAllMocks();
43 | });
44 |
--------------------------------------------------------------------------------
/packages/browser/src/useBrowser.ts:
--------------------------------------------------------------------------------
1 | import { Browser } from '@capacitor/browser';
2 | import { Capacitor } from '@capacitor/core';
3 | import { AvailableResult, notAvailable } from './util/models';
4 | import { isFeatureAvailable, featureNotAvailableError } from './util/feature-check';
5 |
6 | interface CloseResult extends AvailableResult {
7 | close: typeof Browser.close;
8 | }
9 | interface OpenResult extends AvailableResult {
10 | open: typeof Browser.open;
11 | }
12 |
13 | if (!Capacitor.isPluginAvailable('Browser')) {
14 | console.warn('The @capacitor/browser plugin was not found, did you forget to install it?');
15 | }
16 |
17 | export const availableFeatures = {
18 | close: isFeatureAvailable('Browser', 'close'),
19 | open: isFeatureAvailable('Browser', 'open'),
20 | };
21 |
22 | export function useClose(): CloseResult {
23 | if (!availableFeatures.close) {
24 | return {
25 | close: featureNotAvailableError,
26 | ...notAvailable,
27 | };
28 | }
29 |
30 | return {
31 | close: Browser.close,
32 | isAvailable: true,
33 | };
34 | }
35 |
36 | export function useOpen(): OpenResult {
37 | if (!availableFeatures.open) {
38 | return {
39 | open: featureNotAvailableError,
40 | ...notAvailable,
41 | };
42 | }
43 |
44 | return {
45 | open: Browser.open,
46 | isAvailable: true,
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/packages/browser/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Browser: {
13 | open: allTrue,
14 | close: { ...allTrue, web: false },
15 | },
16 | };
17 |
18 | export function isFeatureAvailable<
19 | T extends typeof featureMap,
20 | PluginKeys extends keyof NonNullable,
21 | FeatureKeys extends keyof NonNullable[PluginKeys]>
22 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
23 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
24 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
25 | if (isPluginAvailable && !!isFeatureSupported) {
26 | return true;
27 | }
28 | return false;
29 | }
30 |
31 | export function featureNotAvailableError(): any {
32 | throw new FeatureNotAvailableError();
33 | }
34 |
--------------------------------------------------------------------------------
/packages/browser/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/browser/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/camera/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/camera-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/camera-react@0.0.2...@capacitor-community/camera-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/camera-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/camera-react@0.0.1...@capacitor-community/camera-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/camera-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/camera-react
33 |
--------------------------------------------------------------------------------
/packages/camera/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/camera-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Camera plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/camera": "*",
32 | "@capacitor/core": ">=3.0.0",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/camera/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useCamera';
2 |
--------------------------------------------------------------------------------
/packages/camera/src/useCamera.test.ts:
--------------------------------------------------------------------------------
1 | import { ImageOptions } from '@capacitor/camera';
2 | import { useCamera } from './useCamera';
3 |
4 | import { renderHook, act } from '@testing-library/react-hooks';
5 |
6 | jest.mock('@capacitor/core', () => {
7 | return {
8 | Plugins: {
9 | Camera: {
10 | getPhoto: async (options: ImageOptions) => {
11 | return {
12 | base64String: 'fake',
13 | dataUrl: 'fake',
14 | exif: {},
15 | format: 'jpeg',
16 | path: 'fake',
17 | webPath: 'fake',
18 | };
19 | },
20 | },
21 | },
22 | Capacitor: {
23 | isPluginAvailable: () => true,
24 | platform: 'ios',
25 | },
26 | };
27 | });
28 |
29 | it('Gets photo', async () => {
30 | const { result } = renderHook(() => useCamera());
31 |
32 | await act(async () => {
33 | const { getPhoto, photo, isAvailable } = result.current;
34 |
35 | expect(isAvailable).toBe(true);
36 |
37 | getPhoto({
38 | quality: 90,
39 | allowEditing: true,
40 | resultType: 'base64' as any,
41 | });
42 | });
43 |
44 | await act(async () => {
45 | const { photo } = result.current;
46 |
47 | expect(photo).toMatchObject({
48 | base64String: 'fake',
49 | dataUrl: 'fake',
50 | exif: {},
51 | format: 'jpeg',
52 | path: 'fake',
53 | webPath: 'fake',
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/camera/src/useCamera.ts:
--------------------------------------------------------------------------------
1 | import { Camera, ImageOptions, Photo } from '@capacitor/camera';
2 | import { AvailableResult, notAvailable } from './util/models';
3 | import { isFeatureAvailable, featureNotAvailableError } from './util/feature-check';
4 | import { useState } from 'react';
5 | import { Capacitor } from '@capacitor/core';
6 |
7 | interface CameraResult extends AvailableResult {
8 | photo?: Photo;
9 | getPhoto: (options: ImageOptions) => Promise;
10 | }
11 |
12 | if (!Capacitor.isPluginAvailable('Camera')) {
13 | console.warn('The @capacitor/camera plugin was not found, did you forget to install it?');
14 | }
15 |
16 | export const availableFeatures = {
17 | getPhoto: isFeatureAvailable('Camera', 'getPhoto'),
18 | };
19 |
20 | export function useCamera(): CameraResult {
21 | const [photo, setPhoto] = useState();
22 |
23 | if (!availableFeatures.getPhoto) {
24 | return {
25 | getPhoto: featureNotAvailableError,
26 | ...notAvailable,
27 | };
28 | }
29 |
30 | async function getPhoto(options: ImageOptions) {
31 | const cameraPhoto = await Camera.getPhoto(options);
32 | setPhoto(cameraPhoto);
33 | return cameraPhoto;
34 | }
35 |
36 | return {
37 | photo,
38 | getPhoto,
39 | isAvailable: true,
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/packages/camera/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Camera: {
13 | getPhoto: allTrue,
14 | },
15 | };
16 |
17 | export function isFeatureAvailable<
18 | T extends typeof featureMap,
19 | PluginKeys extends keyof NonNullable,
20 | FeatureKeys extends keyof NonNullable[PluginKeys]>
21 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
22 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
23 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
24 | if (isPluginAvailable && !!isFeatureSupported) {
25 | return true;
26 | }
27 | return false;
28 | }
29 |
30 | export function featureNotAvailableError(): any {
31 | throw new FeatureNotAvailableError();
32 | }
33 |
--------------------------------------------------------------------------------
/packages/camera/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/camera/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/clipboard/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/clipboard-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/clipboard-react@0.0.2...@capacitor-community/clipboard-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/clipboard-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/clipboard-react@0.0.1...@capacitor-community/clipboard-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/clipboard-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/clipboard-react
33 |
--------------------------------------------------------------------------------
/packages/clipboard/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/clipboard-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Clipboard plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/clipboard": "*",
32 | "@capacitor/core": ">=3.0.0",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/clipboard/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useClipboard';
2 |
--------------------------------------------------------------------------------
/packages/clipboard/src/useClipboard.test.ts:
--------------------------------------------------------------------------------
1 | import { useClipboard } from './useClipboard';
2 |
3 | import { renderHook, act } from '@testing-library/react-hooks';
4 |
5 | jest.mock('@capacitor/core', () => {
6 | let text = 'fake';
7 |
8 | return {
9 | Plugins: {
10 | Clipboard: {
11 | text: 'fake',
12 | read: async () => {
13 | return { value: text };
14 | },
15 | write: async ({ string }: { string: string }) => {
16 | text = string;
17 | return {};
18 | },
19 | },
20 | },
21 | Capacitor: {
22 | isPluginAvailable: () => true,
23 | platform: 'ios',
24 | },
25 | };
26 | });
27 |
28 | it('Reads clipboard data', async () => {
29 | const r = renderHook(() => useClipboard());
30 |
31 | await act(async () => {
32 | const result = r.result;
33 | const { getValue, isAvailable } = result.current;
34 | expect(isAvailable).toBe(true);
35 | await getValue();
36 | });
37 |
38 | await act(async () => {
39 | const result = r.result;
40 | expect(result.current.value).toBe('fake');
41 | });
42 | });
43 |
44 | it('Writes clipboard data', async () => {
45 | const { result } = renderHook(() => useClipboard());
46 |
47 | await act(async () => {
48 | const { setValue, isAvailable } = result.current;
49 | expect(isAvailable).toBe(true);
50 | await setValue('testing');
51 | });
52 |
53 | await act(async () => {
54 | const { getValue } = result.current;
55 | await getValue();
56 | });
57 |
58 | await act(async () => {
59 | const { value: data } = result.current;
60 | expect(data).toBe('testing');
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/packages/clipboard/src/useClipboard.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { Clipboard } from '@capacitor/clipboard';
3 | import { AvailableResult, notAvailable } from './util/models';
4 | import { isFeatureAvailable, featureNotAvailableError } from './util/feature-check';
5 | import { Capacitor } from '@capacitor/core';
6 |
7 | interface ClipboardResult extends AvailableResult {
8 | value?: string;
9 | getValue: () => void;
10 | setValue: (value: string) => void;
11 | }
12 |
13 | if (!Capacitor.isPluginAvailable('Clipboard')) {
14 | console.warn('The @capacitor/clipboard plugin was not found, did you forget to install it?');
15 | }
16 | export const availableFeatures = {
17 | useClipboard: isFeatureAvailable('Clipboard', 'useClipboard'),
18 | };
19 |
20 | export function useClipboard(): ClipboardResult {
21 | if (!availableFeatures.useClipboard) {
22 | return {
23 | getValue: featureNotAvailableError,
24 | setValue: featureNotAvailableError,
25 | ...notAvailable,
26 | };
27 | }
28 |
29 | const [data, setData] = useState();
30 |
31 | async function getValue() {
32 | const ret = await Clipboard.read();
33 |
34 | setData(ret.value);
35 | }
36 |
37 | async function setValue(value: string) {
38 | await Clipboard.write({
39 | string: value,
40 | });
41 | }
42 |
43 | return {
44 | value: data,
45 | getValue,
46 | setValue,
47 | isAvailable: true,
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/packages/clipboard/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Clipboard: {
13 | useClipboard: { ...allTrue, web: 'clipboard' in navigator },
14 | },
15 | };
16 |
17 | export function isFeatureAvailable<
18 | T extends typeof featureMap,
19 | PluginKeys extends keyof NonNullable,
20 | FeatureKeys extends keyof NonNullable[PluginKeys]>
21 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
22 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
23 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
24 | if (isPluginAvailable && !!isFeatureSupported) {
25 | return true;
26 | }
27 | return false;
28 | }
29 |
30 | export function featureNotAvailableError(): any {
31 | throw new FeatureNotAvailableError();
32 | }
33 |
--------------------------------------------------------------------------------
/packages/clipboard/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/clipboard/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/device/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/device-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/device-react@0.0.2...@capacitor-community/device-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/device-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/device-react@0.0.1...@capacitor-community/device-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/device-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/device-react
33 |
--------------------------------------------------------------------------------
/packages/device/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/device-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Device plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/device": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/device/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useDevice';
2 |
--------------------------------------------------------------------------------
/packages/device/src/useDevice.test.ts:
--------------------------------------------------------------------------------
1 | import { DeviceInfo, DeviceLanguageCodeResult } from '@capacitor/device';
2 |
3 | jest.mock('@capacitor/core', () => {
4 | return {
5 | Plugins: {
6 | Device: {
7 | getInfo: async (): Promise => {
8 | return {
9 | operatingSystem: 'ios',
10 | diskFree: 12228108288,
11 | osVersion: '11.2',
12 | platform: 'ios',
13 | memUsed: 93851648,
14 | diskTotal: 499054952448,
15 | model: 'iPhone',
16 | manufacturer: 'Apple',
17 | isVirtual: true,
18 | } as any;
19 | },
20 | getLanguageCode: async (): Promise => {
21 | return {
22 | value: 'en',
23 | };
24 | },
25 | },
26 | },
27 | Capacitor: {
28 | isPluginAvailable: () => true,
29 | platform: 'ios',
30 | },
31 | };
32 | });
33 |
34 | import { renderHook, act } from '@testing-library/react-hooks';
35 | import { useGetInfo, useGetLanguageCode } from './useDevice';
36 |
37 | it('Gets device info and language code', async () => {
38 | const r = renderHook(() => useGetInfo());
39 |
40 | await act(async () => {
41 | const result = r.result;
42 | const { isAvailable } = result.current;
43 | expect(isAvailable).toBe(true);
44 | });
45 |
46 | await act(async () => {
47 | const result = r.result;
48 | const { info } = result.current;
49 |
50 | expect(info).toMatchObject({
51 | diskFree: 12228108288,
52 | appVersion: '1.0.2',
53 | appBuild: '123',
54 | osVersion: '11.2',
55 | platform: 'ios',
56 | memUsed: 93851648,
57 | diskTotal: 499054952448,
58 | model: 'iPhone',
59 | manufacturer: 'Apple',
60 | uuid: '84AE7AA1-7000-4696-8A74-4FD588A4A5C7',
61 | isVirtual: true,
62 | });
63 | });
64 | });
65 |
66 | it('Gets device language code', async () => {
67 | const r = renderHook(() => useGetLanguageCode());
68 |
69 | await act(async () => {
70 | const result = r.result;
71 | const { isAvailable } = result.current;
72 | expect(isAvailable).toBe(true);
73 | });
74 |
75 | await act(async () => {
76 | const result = r.result;
77 | const { languageCode } = result.current;
78 | expect(languageCode).toBe('en');
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/packages/device/src/useDevice.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { Device, DeviceInfo } from '@capacitor/device';
3 | import { AvailableResult, notAvailable } from './util/models';
4 | import { isFeatureAvailable } from './util/feature-check';
5 |
6 | interface GetInfoResult extends AvailableResult {
7 | info?: DeviceInfo;
8 | }
9 | interface GetLanguageCodeResult extends AvailableResult {
10 | languageCode?: string;
11 | }
12 |
13 | export const availableFeatures = {
14 | getInfo: isFeatureAvailable('Device', 'getInfo'),
15 | getLanguageCode: isFeatureAvailable('Device', 'getLanguageCode'),
16 | };
17 |
18 | export function useGetInfo(): GetInfoResult {
19 | if (!availableFeatures.getInfo) {
20 | return notAvailable;
21 | }
22 | const [info, setInfo] = useState();
23 |
24 | useEffect(() => {
25 | async function getInfo() {
26 | const data = await Device.getInfo();
27 | setInfo(data);
28 | }
29 | getInfo();
30 | }, [Device, setInfo]);
31 |
32 | return {
33 | info,
34 | isAvailable: true,
35 | };
36 | }
37 |
38 | export function useGetLanguageCode(): GetLanguageCodeResult {
39 | if (!availableFeatures.getLanguageCode) {
40 | return notAvailable;
41 | }
42 |
43 | const [languageCode, setLanguageCode] = useState();
44 |
45 | useEffect(() => {
46 | async function getLanguageCode() {
47 | const data = await Device.getLanguageCode();
48 | setLanguageCode(data.value);
49 | }
50 | getLanguageCode();
51 | }, [Device, setLanguageCode]);
52 |
53 | return {
54 | languageCode,
55 | isAvailable: true,
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/packages/device/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Device: {
13 | getInfo: allTrue,
14 | getLanguageCode: allTrue,
15 | },
16 | };
17 |
18 | export function isFeatureAvailable<
19 | T extends typeof featureMap,
20 | PluginKeys extends keyof NonNullable,
21 | FeatureKeys extends keyof NonNullable[PluginKeys]>
22 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
23 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
24 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
25 | if (isPluginAvailable && !!isFeatureSupported) {
26 | return true;
27 | }
28 | return false;
29 | }
30 |
31 | export function featureNotAvailableError(): any {
32 | throw new FeatureNotAvailableError();
33 | }
34 |
--------------------------------------------------------------------------------
/packages/device/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/device/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/filesystem/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/filesystem-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/filesystem-react@0.0.2...@capacitor-community/filesystem-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/filesystem-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/filesystem-react@0.0.1...@capacitor-community/filesystem-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/filesystem-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/filesystem-react
33 |
--------------------------------------------------------------------------------
/packages/filesystem/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/filesystem-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Filesystem plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/filesystem": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/filesystem/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useFileSystem';
2 | export * from './utils';
3 |
--------------------------------------------------------------------------------
/packages/filesystem/src/useFileSystem.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react';
2 | import {
3 | Filesystem,
4 | WriteFileOptions,
5 | WriteFileResult,
6 | ReadFileOptions,
7 | ReadFileResult,
8 | GetUriOptions,
9 | GetUriResult,
10 | DeleteFileOptions,
11 | } from '@capacitor/filesystem';
12 |
13 | import { AvailableResult } from './util/models';
14 | import { isFeatureAvailable } from './util/feature-check';
15 |
16 | interface FileSystemResult extends AvailableResult {
17 | getUri: (options: GetUriOptions) => Promise;
18 | deleteFile: (options: DeleteFileOptions) => Promise;
19 | readFile: (options: ReadFileOptions) => Promise;
20 | writeFile: (options: WriteFileOptions) => Promise;
21 | }
22 |
23 | export const availableFeatures = {
24 | useFileSystem: isFeatureAvailable('FileSystem', 'useFileSystem'),
25 | };
26 |
27 | export function useFilesystem(): FileSystemResult {
28 | const getUri = useCallback(async (options: GetUriOptions) => {
29 | const result = await Filesystem.getUri(options);
30 | return result;
31 | }, []);
32 |
33 | const deleteFile = useCallback(async (options: DeleteFileOptions) => {
34 | const result = await Filesystem.deleteFile(options);
35 | return result;
36 | }, []);
37 |
38 | const readFile = useCallback(async (options: ReadFileOptions) => {
39 | const result = await Filesystem.readFile(options);
40 | return result;
41 | }, []);
42 |
43 | const writeFile = useCallback(async (options: WriteFileOptions) => {
44 | const result = await Filesystem.writeFile(options);
45 | return result;
46 | }, []);
47 |
48 | return {
49 | getUri,
50 | deleteFile,
51 | readFile,
52 | writeFile,
53 | isAvailable: true,
54 | };
55 | }
56 |
--------------------------------------------------------------------------------
/packages/filesystem/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | FileSystem: {
13 | useFileSystem: allTrue,
14 | },
15 | };
16 |
17 | export function isFeatureAvailable<
18 | T extends typeof featureMap,
19 | PluginKeys extends keyof NonNullable,
20 | FeatureKeys extends keyof NonNullable[PluginKeys]>
21 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
22 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
23 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
24 | if (isPluginAvailable && !!isFeatureSupported) {
25 | return true;
26 | }
27 | return false;
28 | }
29 |
30 | export function featureNotAvailableError(): any {
31 | throw new FeatureNotAvailableError();
32 | }
33 |
--------------------------------------------------------------------------------
/packages/filesystem/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/filesystem/src/utils.ts:
--------------------------------------------------------------------------------
1 | export async function base64FromPath(path: string): Promise {
2 | const response = await fetch(path);
3 | const blob = await response.blob();
4 | return new Promise((resolve, reject) => {
5 | const reader = new FileReader();
6 | reader.onerror = reject;
7 | reader.onload = () => {
8 | if (typeof reader.result === 'string') {
9 | resolve(reader.result);
10 | } else {
11 | reject('method did not return a string');
12 | }
13 | };
14 | reader.readAsDataURL(blob);
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/packages/filesystem/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/geolocation/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/geolocation-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/geolocation-react@0.0.2...@capacitor-community/geolocation-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/geolocation-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/geolocation-react@0.0.1...@capacitor-community/geolocation-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/geolocation-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/geolocation-react
33 |
--------------------------------------------------------------------------------
/packages/geolocation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/geolocation-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor GeoLocation plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/geolocation": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/geolocation/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useGeolocation';
2 |
--------------------------------------------------------------------------------
/packages/geolocation/src/useGeolocation.test.ts:
--------------------------------------------------------------------------------
1 | jest.mock('@capacitor/core', () => {
2 | let watchListener: any;
3 |
4 | const positions = [
5 | [43.0664229, -89.3978106],
6 | [43.0726269, -89.3807787],
7 | [43.0639252, -89.3879085],
8 | [43.0542669, -89.3788027],
9 | ];
10 | let position = positions[0];
11 | let pos = 0;
12 |
13 | return {
14 | Plugins: {
15 | Geolocation: {
16 | positions: positions,
17 | __updatePosition: () => {
18 | position = positions[++pos % positions.length];
19 | watchListener({
20 | timestamp: 1573676975,
21 | coords: {
22 | latitude: position[0],
23 | longitude: position[1],
24 | accuracy: 1,
25 | },
26 | });
27 | },
28 | clearWatch: ({ id }: { id: string }) => {
29 | return;
30 | },
31 | getCurrentPosition: async (options: GeolocationOptions) =>
32 | Promise.resolve({
33 | timestamp: 1573676975,
34 | coords: {
35 | latitude: position[0],
36 | longitude: position[1],
37 | },
38 | }),
39 | watchPosition: async (
40 | options: GeolocationOptions,
41 | cb: (pos: GeolocationPosition, err: any) => void
42 | ) => {
43 | watchListener = cb;
44 | watchListener({
45 | timestamp: 1573676975,
46 | coords: {
47 | latitude: position[0],
48 | longitude: position[1],
49 | accuracy: 1,
50 | },
51 | });
52 |
53 | return 'testid';
54 | },
55 | },
56 | },
57 | Capacitor: {
58 | isPluginAvailable: () => true,
59 | platform: 'ios',
60 | },
61 | };
62 | });
63 |
64 | import { Geolocation, GeolocationOptions, GeolocationPosition } from '@capacitor/geolocation';
65 | import { useWatchPosition, useCurrentPosition } from './useGeolocation';
66 | import { renderHook, act } from '@testing-library/react-hooks';
67 |
68 | it('Gets current geolocation watch position', async () => {
69 | const r = renderHook(() => useWatchPosition());
70 | const geoMock = Geolocation as any;
71 |
72 | function match(pos: GeolocationPosition, coords: [number, number]) {
73 | expect(pos).toMatchObject({
74 | coords: {
75 | latitude: coords[0],
76 | longitude: coords[1],
77 | },
78 | });
79 | }
80 |
81 | await act(async function () {
82 | r.result.current.startWatch();
83 | });
84 |
85 | await act(async function () {
86 | const { currentPosition } = r.result.current;
87 | match(currentPosition!, geoMock.positions[0]);
88 | (Geolocation as any).__updatePosition();
89 | });
90 |
91 | await act(async function () {
92 | const { currentPosition } = r.result.current;
93 | match(currentPosition!, geoMock.positions[1]);
94 | (Geolocation as any).__updatePosition();
95 | });
96 |
97 | await act(async function () {
98 | const { currentPosition } = r.result.current;
99 | match(currentPosition!, geoMock.positions[2]);
100 | (Geolocation as any).__updatePosition();
101 | });
102 |
103 | await act(async function () {
104 | const { currentPosition } = r.result.current;
105 | match(currentPosition!, geoMock.positions[3]);
106 | (Geolocation as any).__updatePosition();
107 | });
108 | });
109 |
110 | it('Gets current geolocation position', async () => {
111 | const r = renderHook(() => useCurrentPosition());
112 | const geoMock = Geolocation as any;
113 |
114 | function match(pos: GeolocationPosition, coords: [number, number]) {
115 | expect(pos).toMatchObject({
116 | coords: {
117 | latitude: coords[0],
118 | longitude: coords[1],
119 | },
120 | });
121 | }
122 |
123 | await act(async function () {
124 | const { isAvailable } = r.result.current;
125 | expect(isAvailable).toBe(true);
126 | });
127 |
128 | await act(async function () {
129 | const { currentPosition } = r.result.current;
130 | match(currentPosition!, geoMock.positions[0]);
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/packages/geolocation/src/useGeolocation.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { Geolocation, Position, PositionOptions } from '@capacitor/geolocation';
3 | import { AvailableResult, notAvailable } from './util/models';
4 | import { isFeatureAvailable, featureNotAvailableError } from './util/feature-check';
5 |
6 | interface GetCurrentPositionResult extends AvailableResult {
7 | error?: any;
8 | currentPosition?: Position;
9 | getPosition: (options?: PositionOptions) => void;
10 | }
11 |
12 | interface GeoWatchPositionResult extends AvailableResult {
13 | error?: any;
14 | currentPosition?: Position;
15 | startWatch: (options?: PositionOptions) => void;
16 | clearWatch: () => void;
17 | }
18 |
19 | export const availableFeatures = {
20 | getCurrentPosition: isFeatureAvailable('Geolocation', 'getCurrentPosition'),
21 | watchPosition: isFeatureAvailable('Geolocation', 'watchPosition'),
22 | };
23 |
24 | export function useCurrentPosition(
25 | options?: PositionOptions,
26 | manual = false
27 | ): GetCurrentPositionResult {
28 | if (!availableFeatures.getCurrentPosition) {
29 | return {
30 | getPosition: featureNotAvailableError,
31 | ...notAvailable,
32 | };
33 | }
34 |
35 | const [currentPosition, setCurrentPosition] = useState();
36 | const [error, setError] = useState();
37 |
38 | const getPosition = async (newOptions?: PositionOptions) => {
39 | let called = false;
40 |
41 | // we use watchPosition here and grab the first result because getCurrentPosition currently has some issues
42 | const id = await Geolocation.watchPosition(
43 | newOptions || options || {},
44 | (pos: Position | null, err) => {
45 | // watchPosition will sometimes fire updates quickly,
46 | // so we check here to make sure its only called one per getPosition invocation
47 | if (!called) {
48 | if (err) {
49 | setError(err);
50 | }
51 |
52 | if (pos) {
53 | setCurrentPosition(pos);
54 | called = true;
55 | // run on next tick so id will be defined
56 | setTimeout(() => Geolocation.clearWatch({ id }), 0);
57 | }
58 | }
59 | }
60 | );
61 | };
62 |
63 | useEffect(() => {
64 | if (!manual) getPosition(options);
65 | }, [options, manual]);
66 |
67 | return {
68 | error,
69 | currentPosition,
70 | getPosition,
71 | isAvailable: true,
72 | };
73 | }
74 |
75 | export function useWatchPosition(): GeoWatchPositionResult {
76 | if (!availableFeatures.watchPosition) {
77 | return {
78 | clearWatch: featureNotAvailableError,
79 | startWatch: featureNotAvailableError,
80 | ...notAvailable,
81 | };
82 | }
83 |
84 | const [currentPosition, setCurrentPosition] = useState();
85 | const [watchId, setWatchId] = useState('');
86 | const [error, setError] = useState();
87 |
88 | const clearWatch = () => {
89 | if (watchId) {
90 | Geolocation.clearWatch({ id: watchId });
91 | setWatchId('');
92 | }
93 | };
94 |
95 | const startWatch = async (options?: PositionOptions) => {
96 | if (!watchId) {
97 | const id = await Geolocation.watchPosition(options || {}, (pos: Position | null, err) => {
98 | if (err) {
99 | setError(err);
100 | }
101 | if (pos) {
102 | setCurrentPosition(pos);
103 | }
104 | });
105 | setWatchId(id);
106 | }
107 | };
108 |
109 | return {
110 | error,
111 | currentPosition,
112 | clearWatch,
113 | startWatch,
114 | isAvailable: true,
115 | };
116 | }
117 |
--------------------------------------------------------------------------------
/packages/geolocation/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Geolocation: {
13 | getCurrentPosition: { ...allTrue, web: 'geolocation' in navigator },
14 | watchPosition: { ...allTrue, web: 'geolocation' in navigator },
15 | },
16 | };
17 |
18 | export function isFeatureAvailable<
19 | T extends typeof featureMap,
20 | PluginKeys extends keyof NonNullable,
21 | FeatureKeys extends keyof NonNullable[PluginKeys]>
22 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
23 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
24 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
25 | if (isPluginAvailable && !!isFeatureSupported) {
26 | return true;
27 | }
28 | return false;
29 | }
30 |
31 | export function featureNotAvailableError(): any {
32 | throw new FeatureNotAvailableError();
33 | }
34 |
--------------------------------------------------------------------------------
/packages/geolocation/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/geolocation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/keyboard/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/keyboard-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/keyboard-react@0.0.2...@capacitor-community/keyboard-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/keyboard-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/keyboard-react@0.0.1...@capacitor-community/keyboard-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/keyboard-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/keyboard-react
33 |
--------------------------------------------------------------------------------
/packages/keyboard/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/keyboard-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Keyboard plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/keyboard": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/keyboard/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useKeyboard';
2 |
--------------------------------------------------------------------------------
/packages/keyboard/src/useKeyboard.test.ts:
--------------------------------------------------------------------------------
1 | import { useKeyboard } from './useKeyboard';
2 | import { renderHook, act } from '@testing-library/react-hooks';
3 |
4 | it('Gets keyboard state', async () => {
5 | const r = renderHook(() => useKeyboard());
6 | await act(async () => {
7 | const state = r.result;
8 | expect(state.current.isOpen).toBe(false);
9 | expect(state.current.keyboardHeight).toBe(0);
10 |
11 | const ev = new CustomEvent('ionKeyboardDidShow', { detail: { keyboardHeight: 150 } });
12 | window.dispatchEvent(ev);
13 | });
14 |
15 | await act(async () => {
16 | const state = r.result.current;
17 | expect(state.isOpen).toBe(true);
18 | expect(state.keyboardHeight).toBe(150);
19 |
20 | const ev = new CustomEvent('ionKeyboardDidHide');
21 | window.dispatchEvent(ev);
22 | });
23 |
24 | await act(async () => {
25 | const state = r.result.current;
26 | expect(state.isOpen).toBe(false);
27 | expect(state.keyboardHeight).toBe(0);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/keyboard/src/useKeyboard.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { Keyboard, KeyboardInfo, KeyboardPlugin } from '@capacitor/keyboard';
3 | import { AvailableResult } from './util/models';
4 | import { Capacitor } from '@capacitor/core';
5 | interface KeyboardResult extends AvailableResult {
6 | isOpen: boolean;
7 | keyboardHeight: number;
8 | keyboard: KeyboardPlugin;
9 | }
10 |
11 | if (!Capacitor.isPluginAvailable('Keyboard')) {
12 | console.warn('The @capacitor/keyboard plugin was not found, did you forget to install it?');
13 | }
14 |
15 | export function useKeyboard(): KeyboardResult {
16 | const [isOpen, setIsOpen] = useState(false);
17 | const [keyboardHeight, setKeyboardHeight] = useState(0);
18 |
19 | useEffect(() => {
20 | const showCallback = (ki: KeyboardInfo) => {
21 | const keyboardHeight = ki.keyboardHeight;
22 | setIsOpen(true);
23 | setKeyboardHeight(keyboardHeight);
24 | };
25 | const hideCallback = () => {
26 | setIsOpen(false);
27 | setKeyboardHeight(0);
28 | };
29 | Keyboard.addListener('keyboardDidShow', showCallback);
30 | Keyboard.addListener('keyboardDidHide', hideCallback);
31 |
32 | return () => {
33 | Keyboard.removeAllListeners();
34 | };
35 | }, [setIsOpen, setKeyboardHeight]);
36 |
37 | return {
38 | isOpen,
39 | keyboardHeight,
40 | isAvailable: true,
41 | keyboard: Keyboard,
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/packages/keyboard/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Keyboard: {},
13 | };
14 |
15 | export function isFeatureAvailable<
16 | T extends typeof featureMap,
17 | PluginKeys extends keyof NonNullable,
18 | FeatureKeys extends keyof NonNullable[PluginKeys]>
19 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
20 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
21 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
22 | if (isPluginAvailable && !!isFeatureSupported) {
23 | return true;
24 | }
25 | return false;
26 | }
27 |
28 | export function featureNotAvailableError(): any {
29 | throw new FeatureNotAvailableError();
30 | }
31 |
--------------------------------------------------------------------------------
/packages/keyboard/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/keyboard/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/network/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/network-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/network-react@0.0.2...@capacitor-community/network-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/network-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/network-react@0.0.1...@capacitor-community/network-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/network-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/network-react
33 |
--------------------------------------------------------------------------------
/packages/network/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/network-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Network plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/network": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/network/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useNetwork';
2 |
--------------------------------------------------------------------------------
/packages/network/src/useNetwork.test.ts:
--------------------------------------------------------------------------------
1 | jest.mock('@capacitor/core', () => {
2 | let listener: any;
3 | const status = {
4 | connected: true,
5 | connectionType: 'wifi',
6 | };
7 | return {
8 | Plugins: {
9 | Network: {
10 | __updateStatus: () => {
11 | status.connected = false;
12 | listener(status);
13 | },
14 | addListener: (eventName: string, cb: (status: ConnectionStatus) => void) => {
15 | listener = cb;
16 | return {
17 | remove: () => {
18 | return;
19 | },
20 | };
21 | },
22 | getStatus: async () => {
23 | return status;
24 | },
25 | },
26 | },
27 | Capacitor: {
28 | isPluginAvailable: () => true,
29 | platform: 'ios',
30 | },
31 | };
32 | });
33 |
34 | import { Network, ConnectionStatus } from '@capacitor/network';
35 |
36 | import { useStatus } from './useNetwork';
37 | import { renderHook, act } from '@testing-library/react-hooks';
38 |
39 | it('Gets current network status', async () => {
40 | const r = renderHook(() => useStatus());
41 |
42 | const networkMock = Network as any;
43 |
44 | await act(async function () {
45 | const { isAvailable } = r.result.current;
46 | expect(isAvailable).toBe(true);
47 | });
48 |
49 | await act(async function () {
50 | const { networkStatus } = r.result.current;
51 | expect(networkStatus).toMatchObject({
52 | connected: true,
53 | connectionType: 'wifi',
54 | });
55 |
56 | networkMock.__updateStatus();
57 | });
58 |
59 | await act(async function () {
60 | const { networkStatus } = r.result.current;
61 |
62 | expect(networkStatus).toMatchObject({
63 | connected: false,
64 | connectionType: 'wifi',
65 | });
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/packages/network/src/useNetwork.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { Network, ConnectionStatus } from '@capacitor/network';
3 | import { AvailableResult, notAvailable } from './util/models';
4 | import { isFeatureAvailable } from './util/feature-check';
5 | import { Capacitor } from '@capacitor/core';
6 |
7 | interface NetworkStatusResult extends AvailableResult {
8 | networkStatus?: ConnectionStatus;
9 | }
10 |
11 | export const availableFeatures = {
12 | getStatus: isFeatureAvailable('Network', 'getStatus'),
13 | };
14 |
15 | if (!Capacitor.isPluginAvailable('Network')) {
16 | console.warn('The @capacitor/network plugin was not found, did you forget to install it?');
17 | }
18 |
19 | export function useStatus(): NetworkStatusResult {
20 | if (!availableFeatures.getStatus) {
21 | return notAvailable;
22 | }
23 |
24 | const [networkStatus, setStatus] = useState();
25 |
26 | useEffect(() => {
27 | async function getStatus() {
28 | const status = await Network.getStatus();
29 | setStatus(status);
30 | }
31 | getStatus();
32 | }, [Network, setStatus]);
33 |
34 | useEffect(() => {
35 | const listener = Network.addListener('networkStatusChange', (status: ConnectionStatus) => {
36 | setStatus(status);
37 | });
38 |
39 | return () => {
40 | listener.remove();
41 | };
42 | }, [Network, setStatus]);
43 |
44 | return {
45 | networkStatus,
46 | isAvailable: true,
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/packages/network/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Network: {
13 | getStatus: allTrue,
14 | },
15 | };
16 |
17 | export function isFeatureAvailable<
18 | T extends typeof featureMap,
19 | PluginKeys extends keyof NonNullable,
20 | FeatureKeys extends keyof NonNullable[PluginKeys]>
21 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
22 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
23 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
24 | if (isPluginAvailable && !!isFeatureSupported) {
25 | return true;
26 | }
27 | return false;
28 | }
29 |
30 | export function featureNotAvailableError(): any {
31 | throw new FeatureNotAvailableError();
32 | }
33 |
--------------------------------------------------------------------------------
/packages/network/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/network/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/screen-reader/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/screen-reader-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/screen-reader-react@0.0.2...@capacitor-community/screen-reader-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/screen-reader-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/screen-reader-react@0.0.1...@capacitor-community/screen-reader-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/screen-reader-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/screen-reader-react
33 |
--------------------------------------------------------------------------------
/packages/screen-reader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/screen-reader-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor ScreenReader plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/screen-reader": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/screen-reader/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useScreenReader';
2 | export * from './util/feature-check';
3 | export * from './util/models';
4 |
--------------------------------------------------------------------------------
/packages/screen-reader/src/useScreenReader.test.ts:
--------------------------------------------------------------------------------
1 | jest.mock('@capacitor/screen-reader', () => {
2 | let listener: any;
3 | let value = true;
4 | return {
5 | ScreenReader: {
6 | __updateStatus: () => {
7 | value = !value;
8 |
9 | listener && listener({ value });
10 | },
11 | isEnabled: async () => {
12 | return { value };
13 | },
14 | addListener(_eventName: string, cb: ({ value }: { value: boolean }) => void) {
15 | listener = cb;
16 | return {
17 | remove: () => {
18 | return;
19 | },
20 | };
21 | },
22 | },
23 | };
24 | });
25 |
26 | import { useIsScreenReaderEnabled } from './useScreenReader';
27 |
28 | import { renderHook, act } from '@testing-library/react-hooks';
29 |
30 | it('Gets screen reader status', async () => {
31 | const r = renderHook(() => useIsScreenReaderEnabled());
32 |
33 | await act(async function () {
34 | const result = r.result;
35 |
36 | const { isScreenReaderEnabled, isAvailable } = result.current;
37 |
38 | expect(isScreenReaderEnabled).toBeUndefined();
39 | expect(isAvailable).toBe(true);
40 | await r.waitForNextUpdate();
41 |
42 | expect(r.result.current.isScreenReaderEnabled).toBe(true);
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/packages/screen-reader/src/useScreenReader.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { ScreenReader, ScreenReaderState } from '@capacitor/screen-reader';
3 | import { Capacitor } from '@capacitor/core';
4 | import { featureNotAvailableError, isFeatureAvailable } from './util/feature-check';
5 | import { AvailableResult, notAvailable } from './util/models';
6 |
7 | interface IsScreenReaderEnabledResult extends AvailableResult {
8 | isScreenReaderEnabled?: boolean;
9 | }
10 | interface SpeakResult extends AvailableResult {
11 | speak: typeof ScreenReader.speak;
12 | }
13 |
14 | if (!Capacitor.isPluginAvailable('ScreenReader')) {
15 | console.warn('The @capacitor/screen-reader plugin was not found, did you forget to install it?');
16 | }
17 |
18 | export const availableFeatures = {
19 | isScreenReaderAvailable: isFeatureAvailable('ScreenReader', 'isEnabled'),
20 | speak: isFeatureAvailable('ScreenReader', 'speak'),
21 | };
22 |
23 | export function useIsScreenReaderEnabled(): IsScreenReaderEnabledResult {
24 | if (!availableFeatures.isScreenReaderAvailable) {
25 | return notAvailable;
26 | }
27 |
28 | const [isEnabled, setIsEnabled] = useState();
29 |
30 | //set initial isEnabled state
31 | useEffect(() => {
32 | async function checkScreenReader() {
33 | const isEnabled = await ScreenReader.isEnabled();
34 | setIsEnabled(isEnabled.value);
35 | }
36 | if (availableFeatures.isScreenReaderAvailable) {
37 | checkScreenReader();
38 | }
39 | }, [ScreenReader, setIsEnabled]);
40 |
41 | //listen for state changes
42 | useEffect(() => {
43 | function checkScreenReader(state: ScreenReaderState) {
44 | setIsEnabled(state.value);
45 | }
46 | ScreenReader.addListener('stateChange', checkScreenReader);
47 | return () => {
48 | ScreenReader.removeAllListeners();
49 | };
50 | }, [ScreenReader, setIsEnabled]);
51 |
52 | return {
53 | isScreenReaderEnabled: isEnabled,
54 | isAvailable: true,
55 | };
56 | }
57 |
58 | export function useSpeak(): SpeakResult {
59 | if (!availableFeatures.speak) {
60 | return {
61 | speak: featureNotAvailableError,
62 | ...notAvailable,
63 | };
64 | }
65 |
66 | return {
67 | speak: ScreenReader.speak,
68 | isAvailable: true,
69 | };
70 | }
71 |
--------------------------------------------------------------------------------
/packages/screen-reader/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | ScreenReader: {
13 | isEnabled: { ...allTrue, web: false },
14 | speak: {
15 | web: 'speechSynthesis' in (typeof window != 'undefined' ? window : {}),
16 | ios: true,
17 | android: true,
18 | electron: true,
19 | },
20 | },
21 | };
22 |
23 | export function isFeatureAvailable<
24 | T extends typeof featureMap,
25 | PluginKeys extends keyof NonNullable,
26 | FeatureKeys extends keyof NonNullable[PluginKeys]>
27 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
28 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
29 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
30 | if (isPluginAvailable && !!isFeatureSupported) {
31 | return true;
32 | }
33 | return false;
34 | }
35 |
36 | export function featureNotAvailableError(): any {
37 | throw new FeatureNotAvailableError();
38 | }
39 |
--------------------------------------------------------------------------------
/packages/screen-reader/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatreNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/screen-reader/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/storage/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package @capacitor-community/storage-react
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/storage-react@0.0.2...@capacitor-community/storage-react@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package @capacitor-community/storage-react
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/@capacitor-community/storage-react@0.0.1...@capacitor-community/storage-react@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package @capacitor-community/storage-react
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package @capacitor-community/storage-react
33 |
--------------------------------------------------------------------------------
/packages/storage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/storage-react",
3 | "version": "0.1.0",
4 | "description": "React Hooks for Capacitor Storage plugin",
5 | "keywords": [
6 | "react",
7 | "capacitor",
8 | "hooks",
9 | "ionic framework",
10 | "ionic"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/capacitor-community/react-hooks.git"
16 | },
17 | "files": [
18 | "dist/"
19 | ],
20 | "main": "dist/index.js",
21 | "scripts": {
22 | "build": "npm run clean && npm run transpile",
23 | "clean": "rimraf dist",
24 | "eslint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
25 | "lint": "npm run eslint",
26 | "lint.fix": "npm run eslint -- --fix",
27 | "test": "echo tests are in process",
28 | "transpile": "tsc"
29 | },
30 | "peerDependencies": {
31 | "@capacitor/core": ">=3.0.0",
32 | "@capacitor/storage": "*",
33 | "react": "*"
34 | },
35 | "prettier": "../../.prettierrc.json",
36 | "eslintConfig": {
37 | "extends": "../../.eslintrc.json"
38 | },
39 | "jest": {
40 | "preset": "ts-jest",
41 | "testPathIgnorePatterns": [
42 | "node_modules",
43 | "dist-transpiled",
44 | "dist",
45 | "test-app"
46 | ],
47 | "modulePaths": [
48 | ""
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/storage/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useStorage';
2 |
--------------------------------------------------------------------------------
/packages/storage/src/useStorage.test.ts:
--------------------------------------------------------------------------------
1 | jest.mock('@capacitor/core', () => {
2 | let data: any = {};
3 | return {
4 | Plugins: {
5 | Storage: {
6 | __init: (d: any) => {
7 | data = d;
8 | },
9 | get: async ({ key }: { key: string }) => {
10 | return { value: data[key] };
11 | },
12 | set: async ({ key, value }: { key: string; value: string }): Promise => {
13 | data[key] = value;
14 | },
15 | remove: async ({ key }: { key: string }) => {
16 | delete data[key];
17 | },
18 | keys: async () => {
19 | return Object.keys(data);
20 | },
21 | clear: async () => {
22 | data = {};
23 | },
24 | },
25 | },
26 | Capacitor: {
27 | isPluginAvailable: () => true,
28 | platform: 'ios',
29 | },
30 | };
31 | });
32 |
33 | import { Plugins } from '@capacitor/core';
34 | import { renderHook, act } from '@testing-library/react-hooks';
35 | import { useStorage, useStorageItem } from './useStorage';
36 |
37 | it('Gets and sets storage values', async () => {
38 | const r = renderHook(() => useStorage());
39 |
40 | await act(async () => {
41 | const result = r.result.current;
42 | const { isAvailable } = result;
43 | expect(isAvailable).toBe(true);
44 | });
45 |
46 | await act(async () => {
47 | const result = r.result.current;
48 |
49 | const { get, set, remove, getKeys, clear } = result;
50 |
51 | await set('name', 'Max');
52 |
53 | let name = await get('name');
54 | expect(name).toEqual('Max');
55 |
56 | await remove('name');
57 | name = await get('name');
58 | expect(name).toEqual(undefined);
59 |
60 | await set('name', 'Max');
61 | const knownKeys = await getKeys();
62 | expect(knownKeys).toStrictEqual(['name']);
63 |
64 | await clear();
65 | name = await get('name');
66 | expect(name).toEqual(undefined);
67 | });
68 | });
69 |
70 | it('Manages individual item', async () => {
71 | let r: any;
72 | await act(async () => {
73 | r = renderHook(() => useStorageItem('name', 'Max'));
74 | });
75 |
76 | await act(async () => {
77 | return;
78 | });
79 |
80 | await act(async () => {
81 | const result = r.result.current;
82 |
83 | const [value, setValue] = result;
84 | expect(value).toBe('Max');
85 |
86 | setValue('Frank');
87 | });
88 |
89 | await act(async () => {
90 | const result = r.result.current;
91 |
92 | const [value] = result;
93 | expect(value).toBe('Frank');
94 | });
95 | });
96 |
97 | it('Manages individual item with stored value', async () => {
98 | let r: any;
99 |
100 | const storageMock = Plugins.Storage as any;
101 | await act(async () => {
102 | storageMock.__init({ name: 'Matilda' });
103 | });
104 |
105 | await act(async () => {
106 | r = renderHook(() => useStorageItem('name', 'Max'));
107 | });
108 |
109 | await act(async () => {
110 | return;
111 | });
112 |
113 | await act(async () => {
114 | const result = r.result.current;
115 |
116 | const [value, setValue] = result;
117 | expect(value).toBe('Matilda');
118 |
119 | setValue('Frank');
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/packages/storage/src/useStorage.ts:
--------------------------------------------------------------------------------
1 | // Inspired by useLocalStorage from https://usehooks.com/useLocalStorage/
2 | import { useState, useEffect, useCallback } from 'react';
3 | import { Storage } from '@capacitor/storage';
4 | import { AvailableResult, notAvailable } from './util/models';
5 | import { isFeatureAvailable, featureNotAvailableError } from './util/feature-check';
6 | import { Capacitor } from '@capacitor/core';
7 |
8 | interface StorageResult extends AvailableResult {
9 | get: (key: string) => Promise;
10 | set: (key: string, value: string) => Promise;
11 | remove: (key: string) => void;
12 | getKeys: () => Promise<{ keys: string[] }>;
13 | clear: () => Promise;
14 | }
15 |
16 | type StorageItemResult = [T | undefined, (value: T) => Promise, boolean];
17 |
18 | if (!Capacitor.isPluginAvailable('Storage')) {
19 | console.warn('The @capacitor/storage plugin was not found, did you forget to install it?');
20 | }
21 |
22 | export const availableFeatures = {
23 | useStorage: isFeatureAvailable('Storage', 'useStorage'),
24 | };
25 |
26 | export function useStorage(): StorageResult {
27 | if (!availableFeatures.useStorage) {
28 | return {
29 | get: featureNotAvailableError,
30 | set: featureNotAvailableError,
31 | remove: featureNotAvailableError,
32 | getKeys: featureNotAvailableError,
33 | clear: featureNotAvailableError,
34 | ...notAvailable,
35 | };
36 | }
37 |
38 | const get = useCallback(async (key: string) => {
39 | const v = await Storage.get({ key });
40 | if (v) {
41 | return v.value;
42 | }
43 | return null;
44 | }, []);
45 |
46 | const set = useCallback((key: string, value: string) => {
47 | return Storage.set({ key, value: value });
48 | }, []);
49 |
50 | const remove = useCallback((key: string) => {
51 | return Storage.remove({ key });
52 | }, []);
53 |
54 | const getKeys = useCallback(() => {
55 | return Storage.keys();
56 | }, []);
57 |
58 | const clear = useCallback(() => {
59 | return Storage.clear();
60 | }, []);
61 |
62 | return { get, set, remove, getKeys, clear, isAvailable: true };
63 | }
64 |
65 | export function useStorageItem(key: string, initialValue?: T): StorageItemResult {
66 | if (!availableFeatures.useStorage) {
67 | return [undefined, featureNotAvailableError, false];
68 | }
69 |
70 | const [storedValue, setStoredValue] = useState();
71 |
72 | useEffect(() => {
73 | async function loadValue() {
74 | try {
75 | const result = await Storage.get({ key });
76 | if (result.value == undefined && initialValue != undefined) {
77 | result.value =
78 | typeof initialValue === 'string' ? initialValue : JSON.stringify(initialValue);
79 | setValue(result.value as any);
80 | } else {
81 | if (result.value) {
82 | setStoredValue(
83 | typeof result.value === 'string' ? result.value : JSON.parse(result.value)
84 | );
85 | } else {
86 | setStoredValue(undefined);
87 | }
88 | }
89 | } catch (e) {
90 | return initialValue;
91 | }
92 | }
93 | loadValue();
94 | }, [Storage, setStoredValue, initialValue, key]);
95 |
96 | const setValue = async (value: T) => {
97 | try {
98 | setStoredValue(value);
99 | await Storage.set({ key, value: typeof value === 'string' ? value : JSON.stringify(value) });
100 | } catch (e) {
101 | console.error(e);
102 | }
103 | };
104 |
105 | return [storedValue, setValue, true];
106 | }
107 |
--------------------------------------------------------------------------------
/packages/storage/src/util/feature-check.ts:
--------------------------------------------------------------------------------
1 | import { Capacitor } from '@capacitor/core';
2 | import { FeatureNotAvailableError } from './models';
3 |
4 | const allTrue = {
5 | web: true,
6 | ios: true,
7 | android: true,
8 | electron: true,
9 | };
10 |
11 | const featureMap = {
12 | Storage: {
13 | useStorage: allTrue,
14 | },
15 | };
16 |
17 | export function isFeatureAvailable<
18 | T extends typeof featureMap,
19 | PluginKeys extends keyof NonNullable,
20 | FeatureKeys extends keyof NonNullable[PluginKeys]>
21 | >(plugin: PluginKeys, method: FeatureKeys): boolean {
22 | const isPluginAvailable = Capacitor.isPluginAvailable(plugin as string);
23 | const isFeatureSupported = (featureMap as any)[plugin][method][Capacitor.getPlatform()];
24 | if (isPluginAvailable && !!isFeatureSupported) {
25 | return true;
26 | }
27 | return false;
28 | }
29 |
30 | export function featureNotAvailableError(): any {
31 | throw new FeatureNotAvailableError();
32 | }
33 |
--------------------------------------------------------------------------------
/packages/storage/src/util/models.ts:
--------------------------------------------------------------------------------
1 | export interface AvailableResult {
2 | isAvailable: boolean;
3 | }
4 |
5 | export const notAvailable = {
6 | isAvailable: false,
7 | };
8 |
9 | export class FeatureNotAvailableError extends Error {
10 | constructor() {
11 | super();
12 | this.message =
13 | 'Feature not available on this platform/device. Check availability before attempting to call this method';
14 | this.name = 'FeatureNotAvailableError';
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/storage/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "files": ["src/index.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/test-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 | .vscode
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/packages/test-app/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # 0.1.0 (2021-10-17)
7 |
8 | **Note:** Version bump only for package react-ionic-hooks-test-app
9 |
10 |
11 |
12 |
13 |
14 | ## [0.0.3](https://github.com/capacitor-community/react-hooks/compare/react-ionic-hooks-test-app@0.0.2...react-ionic-hooks-test-app@0.0.3) (2021-10-17)
15 |
16 | **Note:** Version bump only for package react-ionic-hooks-test-app
17 |
18 |
19 |
20 |
21 |
22 | ## [0.0.2](https://github.com/capacitor-community/react-hooks/compare/react-ionic-hooks-test-app@0.0.1...react-ionic-hooks-test-app@0.0.2) (2021-10-17)
23 |
24 | **Note:** Version bump only for package react-ionic-hooks-test-app
25 |
26 |
27 |
28 |
29 |
30 | ## 0.0.1 (2021-10-16)
31 |
32 | **Note:** Version bump only for package react-ionic-hooks-test-app
33 |
--------------------------------------------------------------------------------
/packages/test-app/capacitor.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "appId": "io.ionic.starter",
3 | "appName": "react-ionic-hooks",
4 | "bundledWebRuntime": false,
5 | "npmClient": "npm",
6 | "webDir": "build",
7 | "cordova": {}
8 | }
9 |
--------------------------------------------------------------------------------
/packages/test-app/ionic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ionic-hooks",
3 | "integrations": {
4 | "capacitor": {}
5 | },
6 | "type": "react"
7 | }
8 |
--------------------------------------------------------------------------------
/packages/test-app/ios/.gitignore:
--------------------------------------------------------------------------------
1 | App/build
2 | App/Pods
3 | App/Podfile.lock
4 | App/App/public
5 | DerivedData
6 | xcuserdata
7 |
8 | # Cordova plugins for Capacitor
9 | capacitor-cordova-ios-plugins
10 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Capacitor
3 |
4 | @UIApplicationMain
5 | class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
10 | // Override point for customization after application launch.
11 | return true
12 | }
13 |
14 | func applicationWillResignActive(_ application: UIApplication) {
15 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
16 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
17 | }
18 |
19 | func applicationDidEnterBackground(_ application: UIApplication) {
20 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
21 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
22 | }
23 |
24 | func applicationWillEnterForeground(_ application: UIApplication) {
25 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
26 | }
27 |
28 | func applicationDidBecomeActive(_ application: UIApplication) {
29 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
30 | }
31 |
32 | func applicationWillTerminate(_ application: UIApplication) {
33 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
34 | }
35 |
36 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
37 | // Called when the app was launched with a url. Feel free to add additional processing here,
38 | // but if you want the App API to support tracking app url opens, make sure to keep this call
39 | return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
40 | }
41 |
42 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
43 | // Called when the app was launched with an activity, including Universal Links.
44 | // Feel free to add additional processing here, but if you want the App API to support
45 | // tracking app url opens, make sure to keep this call
46 | return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
47 | }
48 |
49 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
50 | super.touchesBegan(touches, with: event)
51 |
52 | let statusBarRect = UIApplication.shared.statusBarFrame
53 | guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }
54 |
55 | if statusBarRect.contains(touchPoint) {
56 | NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil)
57 | }
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "AppIcon-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "AppIcon-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "AppIcon-29x29@2x-1.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "AppIcon-29x29@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "AppIcon-40x40@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "AppIcon-40x40@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "AppIcon-60x60@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "AppIcon-60x60@3x.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "AppIcon-20x20@1x.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "AppIcon-20x20@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "AppIcon-29x29@1x.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "AppIcon-29x29@2x.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "AppIcon-40x40@1x.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "AppIcon-40x40@2x-1.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "AppIcon-76x76@1x.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "AppIcon-76x76@2x.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "AppIcon-83.5x83.5@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "AppIcon-512@2x.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "splash-2732x2732-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "splash-2732x2732-1.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "splash-2732x2732.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | react-ionic-hooks
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UISupportedInterfaceOrientations~ipad
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationPortraitUpsideDown
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 | NSCameraUsageDescription
52 | to take a pic
53 | NSPhotoLibraryAddUsageDescription
54 | to look at your pics
55 | UIViewControllerBasedStatusBarAppearance
56 |
57 | NSPhotoLibraryUsageDescription
58 | moar pics
59 | NSLocationAlwaysAndWhenInUseUsageDescription
60 | know where you are at always and now
61 | NSLocationWhenInUseUsageDescription
62 | know where you are at now and forever
63 | NSLocationAlwaysUsageDescription
64 | know where you are at always
65 |
66 |
67 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/capacitor.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "appId": "io.ionic.starter",
3 | "appName": "react-ionic-hooks",
4 | "bundledWebRuntime": false,
5 | "npmClient": "npm",
6 | "webDir": "build",
7 | "cordova": {}
8 | }
9 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/App/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/test-app/ios/App/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '12.0'
2 | use_frameworks!
3 |
4 | # workaround to avoid Xcode caching of Pods that requires
5 | # Product -> Clean Build Folder after new Cordova plugins installed
6 | # Requires CocoaPods 1.6 or newer
7 | install! 'cocoapods', :disable_input_output_paths => true
8 |
9 | def capacitor_pods
10 | pod 'Capacitor', :path => '../../../../node_modules/@capacitor/ios'
11 | pod 'CapacitorCordova', :path => '../../../../node_modules/@capacitor/ios'
12 | pod 'CapacitorApp', :path => '../../../../node_modules/@capacitor/app'
13 | pod 'CapacitorBrowser', :path => '../../../../node_modules/@capacitor/browser'
14 | pod 'CapacitorCamera', :path => '../../../../node_modules/@capacitor/camera'
15 | pod 'CapacitorClipboard', :path => '../../../../node_modules/@capacitor/clipboard'
16 | pod 'CapacitorDevice', :path => '../../../../node_modules/@capacitor/device'
17 | pod 'CapacitorFilesystem', :path => '../../../../node_modules/@capacitor/filesystem'
18 | pod 'CapacitorGeolocation', :path => '../../../../node_modules/@capacitor/geolocation'
19 | pod 'CapacitorKeyboard', :path => '../../../../node_modules/@capacitor/keyboard'
20 | pod 'CapacitorNetwork', :path => '../../../../node_modules/@capacitor/network'
21 | pod 'CapacitorScreenReader', :path => '../../../../node_modules/@capacitor/screen-reader'
22 | pod 'CapacitorStorage', :path => '../../../../node_modules/@capacitor/storage'
23 | end
24 |
25 | target 'App' do
26 | capacitor_pods
27 | # Add your Pods here
28 | end
29 |
--------------------------------------------------------------------------------
/packages/test-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ionic-hooks-test-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@capacitor-community/app-react": "file:../app",
7 | "@capacitor-community/browser-react": "file:../browser",
8 | "@capacitor-community/camera-react": "file:../camera",
9 | "@capacitor-community/clipboard-react": "file:../clipboard",
10 | "@capacitor-community/device-react": "file:../device",
11 | "@capacitor-community/filesystem-react": "file:../filesystem",
12 | "@capacitor-community/geolocation-react": "file:../geolocation",
13 | "@capacitor-community/keyboard-react": "file:../keyboard",
14 | "@capacitor-community/network-react": "file:../network",
15 | "@capacitor-community/screen-reader-react": "file:../screen-reader",
16 | "@capacitor-community/storage-react": "file:../storage",
17 | "@capacitor/app": "^1.0.4",
18 | "@capacitor/browser": "^1.0.4",
19 | "@capacitor/camera": "^1.1.1",
20 | "@capacitor/clipboard": "^1.0.4",
21 | "@capacitor/core": "^3.2.4",
22 | "@capacitor/device": "^1.0.4",
23 | "@capacitor/filesystem": "^1.0.4",
24 | "@capacitor/geolocation": "^1.1.0",
25 | "@capacitor/ios": "^3.2.4",
26 | "@capacitor/keyboard": "^1.1.0",
27 | "@capacitor/network": "^1.0.3",
28 | "@capacitor/screen-reader": "^1.0.3",
29 | "@capacitor/storage": "^1.2.0",
30 | "@ionic/pwa-elements": "^3.0.2",
31 | "@ionic/react": "^5.8.4",
32 | "@ionic/react-router": "^5.8.4",
33 | "@types/jest": "^24.0.18",
34 | "@types/node": "^12.7.12",
35 | "@types/react": "^17.0.29",
36 | "@types/react-dom": "^17.0.9",
37 | "@types/react-router": "^5.1.1",
38 | "@types/react-router-dom": "^5.1.0",
39 | "ionicons": "^5.5.3",
40 | "react": "17.0.2",
41 | "react-dom": "17.0.2",
42 | "react-router": "^5.1.0",
43 | "react-router-dom": "^5.1.0",
44 | "react-scripts": "^4.0.3",
45 | "typescript": "^4.4.4"
46 | },
47 | "scripts": {
48 | "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start",
49 | "build": "CI=false SKIP_PREFLIGHT_CHECK=true react-scripts build",
50 | "test": "SKIP_PREFLIGHT_CHECK=true react-scripts test",
51 | "eject": "react-scripts eject"
52 | },
53 | "browserslist": {
54 | "production": [
55 | ">0.2%",
56 | "not dead",
57 | "not op_mini all"
58 | ],
59 | "development": [
60 | "last 1 chrome version",
61 | "last 1 firefox version",
62 | "last 1 safari version"
63 | ]
64 | },
65 | "description": "An Ionic project"
66 | }
67 |
--------------------------------------------------------------------------------
/packages/test-app/public/assets/icon/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/public/assets/icon/favicon.png
--------------------------------------------------------------------------------
/packages/test-app/public/assets/shapes.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/test-app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/react-hooks/bfdf2b7b78651fd1f7c1ce53d4986b7b3148b0ee/packages/test-app/public/favicon.ico
--------------------------------------------------------------------------------
/packages/test-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ionic App
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/packages/test-app/scripts/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'production';
5 | process.env.NODE_ENV = 'production';
6 |
7 | // Makes the script crash on unhandled rejections instead of silently
8 | // ignoring them. In the future, promise rejections that are not handled will
9 | // terminate the Node.js process with a non-zero exit code.
10 | process.on('unhandledRejection', err => {
11 | throw err;
12 | });
13 |
14 | // Ensure environment variables are read.
15 | require('../config/env');
16 |
17 |
18 | const path = require('path');
19 | const chalk = require('react-dev-utils/chalk');
20 | const fs = require('fs-extra');
21 | const webpack = require('webpack');
22 | const configFactory = require('../config/webpack.config');
23 | const paths = require('../config/paths');
24 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
25 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
26 | const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
27 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
28 | const printBuildError = require('react-dev-utils/printBuildError');
29 |
30 | const measureFileSizesBeforeBuild =
31 | FileSizeReporter.measureFileSizesBeforeBuild;
32 | const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
33 | const useYarn = fs.existsSync(paths.yarnLockFile);
34 |
35 | // These sizes are pretty large. We'll warn for bundles exceeding them.
36 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
37 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
38 |
39 | const isInteractive = process.stdout.isTTY;
40 |
41 | // Warn and crash if required files are missing
42 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
43 | process.exit(1);
44 | }
45 |
46 | // Generate configuration
47 | const config = configFactory('production');
48 |
49 | // We require that you explicitly set browsers and do not fall back to
50 | // browserslist defaults.
51 | const { checkBrowsers } = require('react-dev-utils/browsersHelper');
52 | checkBrowsers(paths.appPath, isInteractive)
53 | .then(() => {
54 | // First, read the current file sizes in build directory.
55 | // This lets us display how much they changed later.
56 | return measureFileSizesBeforeBuild(paths.appBuild);
57 | })
58 | .then(previousFileSizes => {
59 | // Remove all content but keep the directory so that
60 | // if you're in it, you don't end up in Trash
61 | fs.emptyDirSync(paths.appBuild);
62 | // Merge with the public folder
63 | copyPublicFolder();
64 | // Start the webpack build
65 | return build(previousFileSizes);
66 | })
67 | .then(
68 | ({ stats, previousFileSizes, warnings }) => {
69 | if (warnings.length) {
70 | console.log(chalk.yellow('Compiled with warnings.\n'));
71 | console.log(warnings.join('\n\n'));
72 | console.log(
73 | '\nSearch for the ' +
74 | chalk.underline(chalk.yellow('keywords')) +
75 | ' to learn more about each warning.'
76 | );
77 | console.log(
78 | 'To ignore, add ' +
79 | chalk.cyan('// eslint-disable-next-line') +
80 | ' to the line before.\n'
81 | );
82 | } else {
83 | console.log(chalk.green('Compiled successfully.\n'));
84 | }
85 |
86 | console.log('File sizes after gzip:\n');
87 | printFileSizesAfterBuild(
88 | stats,
89 | previousFileSizes,
90 | paths.appBuild,
91 | WARN_AFTER_BUNDLE_GZIP_SIZE,
92 | WARN_AFTER_CHUNK_GZIP_SIZE
93 | );
94 | console.log();
95 |
96 | const appPackage = require(paths.appPackageJson);
97 | const publicUrl = paths.publicUrl;
98 | const publicPath = config.output.publicPath;
99 | const buildFolder = path.relative(process.cwd(), paths.appBuild);
100 | printHostingInstructions(
101 | appPackage,
102 | publicUrl,
103 | publicPath,
104 | buildFolder,
105 | useYarn
106 | );
107 | },
108 | err => {
109 | const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
110 | if (tscCompileOnError) {
111 | console.log(chalk.yellow(
112 | 'Compiled with the following type errors (you may want to check these before deploying your app):\n'
113 | ));
114 | printBuildError(err);
115 | } else {
116 | console.log(chalk.red('Failed to compile.\n'));
117 | printBuildError(err);
118 | process.exit(1);
119 | }
120 | }
121 | )
122 | .catch(err => {
123 | if (err && err.message) {
124 | console.log(err.message);
125 | }
126 | process.exit(1);
127 | });
128 |
129 | // Create the production build and print the deployment instructions.
130 | function build(previousFileSizes) {
131 | // We used to support resolving modules according to `NODE_PATH`.
132 | // This now has been deprecated in favor of jsconfig/tsconfig.json
133 | // This lets you use absolute paths in imports inside large monorepos:
134 | if (process.env.NODE_PATH) {
135 | console.log(
136 | chalk.yellow(
137 | 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
138 | )
139 | );
140 | console.log();
141 | }
142 |
143 | console.log('Creating an optimized production build...');
144 |
145 | const compiler = webpack(config);
146 | return new Promise((resolve, reject) => {
147 | compiler.run((err, stats) => {
148 | let messages;
149 | if (err) {
150 | if (!err.message) {
151 | return reject(err);
152 | }
153 | messages = formatWebpackMessages({
154 | errors: [err.message],
155 | warnings: [],
156 | });
157 | } else {
158 | messages = formatWebpackMessages(
159 | stats.toJson({ all: false, warnings: true, errors: true })
160 | );
161 | }
162 | if (messages.errors.length) {
163 | // Only keep the first error. Others are often indicative
164 | // of the same problem, but confuse the reader with noise.
165 | if (messages.errors.length > 1) {
166 | messages.errors.length = 1;
167 | }
168 | return reject(new Error(messages.errors.join('\n\n')));
169 | }
170 | if (
171 | process.env.CI &&
172 | (typeof process.env.CI !== 'string' ||
173 | process.env.CI.toLowerCase() !== 'false') &&
174 | messages.warnings.length
175 | ) {
176 | console.log(
177 | chalk.yellow(
178 | '\nTreating warnings as errors because process.env.CI = true.\n' +
179 | 'Most CI servers set it automatically.\n'
180 | )
181 | );
182 | return reject(new Error(messages.warnings.join('\n\n')));
183 | }
184 |
185 | return resolve({
186 | stats,
187 | previousFileSizes,
188 | warnings: messages.warnings,
189 | });
190 | });
191 | });
192 | }
193 |
194 | function copyPublicFolder() {
195 | fs.copySync(paths.appPublic, paths.appBuild, {
196 | dereference: true,
197 | filter: file => file !== paths.appHtml,
198 | });
199 | }
200 |
--------------------------------------------------------------------------------
/packages/test-app/scripts/start.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'development';
5 | process.env.NODE_ENV = 'development';
6 |
7 | // Makes the script crash on unhandled rejections instead of silently
8 | // ignoring them. In the future, promise rejections that are not handled will
9 | // terminate the Node.js process with a non-zero exit code.
10 | process.on('unhandledRejection', err => {
11 | throw err;
12 | });
13 |
14 | // Ensure environment variables are read.
15 | require('../config/env');
16 |
17 |
18 | const fs = require('fs');
19 | const chalk = require('react-dev-utils/chalk');
20 | const webpack = require('webpack');
21 | const WebpackDevServer = require('webpack-dev-server');
22 | const clearConsole = require('react-dev-utils/clearConsole');
23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
24 | const {
25 | choosePort,
26 | createCompiler,
27 | prepareProxy,
28 | prepareUrls,
29 | } = require('react-dev-utils/WebpackDevServerUtils');
30 | const openBrowser = require('react-dev-utils/openBrowser');
31 | const paths = require('../config/paths');
32 | const configFactory = require('../config/webpack.config');
33 | const createDevServerConfig = require('../config/webpackDevServer.config');
34 |
35 | const useYarn = fs.existsSync(paths.yarnLockFile);
36 | const isInteractive = process.stdout.isTTY;
37 |
38 | // Warn and crash if required files are missing
39 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
40 | process.exit(1);
41 | }
42 |
43 | // Tools like Cloud9 rely on this.
44 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
45 | const HOST = process.env.HOST || '0.0.0.0';
46 |
47 | if (process.env.HOST) {
48 | console.log(
49 | chalk.cyan(
50 | `Attempting to bind to HOST environment variable: ${chalk.yellow(
51 | chalk.bold(process.env.HOST)
52 | )}`
53 | )
54 | );
55 | console.log(
56 | `If this was unintentional, check that you haven't mistakenly set it in your shell.`
57 | );
58 | console.log(
59 | `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
60 | );
61 | console.log();
62 | }
63 |
64 | // We require that you explicitly set browsers and do not fall back to
65 | // browserslist defaults.
66 | const { checkBrowsers } = require('react-dev-utils/browsersHelper');
67 | checkBrowsers(paths.appPath, isInteractive)
68 | .then(() => {
69 | // We attempt to use the default port but if it is busy, we offer the user to
70 | // run on a different port. `choosePort()` Promise resolves to the next free port.
71 | return choosePort(HOST, DEFAULT_PORT);
72 | })
73 | .then(port => {
74 | if (port == null) {
75 | // We have not found a port.
76 | return;
77 | }
78 | const config = configFactory('development');
79 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
80 | const appName = require(paths.appPackageJson).name;
81 | const useTypeScript = fs.existsSync(paths.appTsConfig);
82 | const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
83 | const urls = prepareUrls(protocol, HOST, port);
84 | const devSocket = {
85 | warnings: warnings =>
86 | devServer.sockWrite(devServer.sockets, 'warnings', warnings),
87 | errors: errors =>
88 | devServer.sockWrite(devServer.sockets, 'errors', errors),
89 | };
90 | // Create a webpack compiler that is configured with custom messages.
91 | const compiler = createCompiler({
92 | appName,
93 | config,
94 | devSocket,
95 | urls,
96 | useYarn,
97 | useTypeScript,
98 | tscCompileOnError,
99 | webpack,
100 | });
101 | // Load proxy config
102 | const proxySetting = require(paths.appPackageJson).proxy;
103 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
104 | // Serve webpack assets generated by the compiler over a web server.
105 | const serverConfig = createDevServerConfig(
106 | proxyConfig,
107 | urls.lanUrlForConfig
108 | );
109 | const devServer = new WebpackDevServer(compiler, serverConfig);
110 | // Launch WebpackDevServer.
111 | devServer.listen(port, HOST, err => {
112 | if (err) {
113 | return console.log(err);
114 | }
115 | if (isInteractive) {
116 | clearConsole();
117 | }
118 |
119 | // We used to support resolving modules according to `NODE_PATH`.
120 | // This now has been deprecated in favor of jsconfig/tsconfig.json
121 | // This lets you use absolute paths in imports inside large monorepos:
122 | if (process.env.NODE_PATH) {
123 | console.log(
124 | chalk.yellow(
125 | 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
126 | )
127 | );
128 | console.log();
129 | }
130 |
131 | console.log(chalk.cyan('Starting the development server...\n'));
132 | openBrowser(urls.localUrlForBrowser);
133 | });
134 |
135 | ['SIGINT', 'SIGTERM'].forEach(function(sig) {
136 | process.on(sig, function() {
137 | devServer.close();
138 | process.exit();
139 | });
140 | });
141 | })
142 | .catch(err => {
143 | if (err && err.message) {
144 | console.log(err.message);
145 | }
146 | process.exit(1);
147 | });
148 |
--------------------------------------------------------------------------------
/packages/test-app/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--watchAll') === -1 &&
45 | argv.indexOf('--watchAll=false') === -1
46 | ) {
47 | // https://github.com/facebook/create-react-app/issues/5210
48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
50 | }
51 |
52 |
53 | jest.run(argv);
54 |
--------------------------------------------------------------------------------
/packages/test-app/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/packages/test-app/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Redirect, Route } from 'react-router-dom';
3 | import { IonApp, IonRouterOutlet, IonSplitPane } from '@ionic/react';
4 | import { IonReactRouter } from '@ionic/react-router';
5 |
6 | import Menu from './components/Menu';
7 | import Home from './pages/Home';
8 | import ScreenReaderPage from './pages/ScreenReaderPage';
9 |
10 | /* Core CSS required for Ionic components to work properly */
11 | import '@ionic/react/css/core.css';
12 |
13 | /* Basic CSS for apps built with Ionic */
14 | import '@ionic/react/css/normalize.css';
15 | import '@ionic/react/css/structure.css';
16 | import '@ionic/react/css/typography.css';
17 |
18 | /* Optional CSS utils that can be commented out */
19 | import '@ionic/react/css/padding.css';
20 | import '@ionic/react/css/float-elements.css';
21 | import '@ionic/react/css/text-alignment.css';
22 | import '@ionic/react/css/text-transformation.css';
23 | import '@ionic/react/css/flex-utils.css';
24 | import '@ionic/react/css/display.css';
25 |
26 | /* Theme variables */
27 | import './theme/variables.css';
28 | import AppPage from './pages/AppPage';
29 | import BrowserPage from './pages/BrowserPage';
30 | import CameraPage from './pages/CameraPage';
31 | import ClipboardPage from './pages/ClipboardPage';
32 | import DevicePage from './pages/DevicePage';
33 | import GeolocationPage from './pages/GeolocationPage';
34 | import KeyboardPage from './pages/KeyboardPage';
35 | import NetworkPage from './pages/NetworkPage';
36 | import StoragePage from './pages/StoragePage';
37 |
38 | const App: React.FC = () => (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 |
62 | export default App;
63 |
--------------------------------------------------------------------------------
/packages/test-app/src/components/Menu.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonContent,
3 | IonHeader,
4 | IonIcon,
5 | IonItem,
6 | IonLabel,
7 | IonList,
8 | IonMenu,
9 | IonMenuToggle,
10 | IonTitle,
11 | IonToolbar,
12 | } from '@ionic/react';
13 | import React from 'react';
14 | import { RouteComponentProps, withRouter } from 'react-router-dom';
15 | import { star } from 'ionicons/icons';
16 |
17 | type MenuProps = RouteComponentProps;
18 |
19 | const Menu: React.FC = () => (
20 |
21 |
22 |
23 | Menu
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | App
32 |
33 |
34 |
35 | Browser
36 |
37 |
38 |
39 | Camera
40 |
41 |
42 |
43 | Clipboard
44 |
45 |
46 |
47 | Device
48 |
49 |
50 |
51 | Geolocation
52 |
53 |
54 |
55 | Keyboard
56 |
57 |
58 |
59 | Network
60 |
61 |
62 |
63 | ScreenReader
64 |
65 |
66 |
67 | Storage
68 |
69 |
70 |
71 |
72 |
73 | );
74 |
75 | export default withRouter(Menu);
76 |
--------------------------------------------------------------------------------
/packages/test-app/src/declarations.ts:
--------------------------------------------------------------------------------
1 | export interface AppPage {
2 | url: string;
3 | icon: object;
4 | title: string;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/test-app/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import { defineCustomElements } from '@ionic/pwa-elements/loader';
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 |
8 | defineCustomElements(window);
9 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/AppPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | } from '@ionic/react';
15 | import { Capacitor } from '@capacitor/core';
16 | import {
17 | useAppState,
18 | useAppUrlOpen,
19 | useLaunchUrl,
20 | availableFeatures,
21 | } from '@capacitor-community/app-react';
22 | const platform = Capacitor.getPlatform();
23 | const AppPage: React.FC = () => {
24 | const { state } = useAppState();
25 | const { appUrlOpen } = useAppUrlOpen();
26 | const { launchUrl } = useLaunchUrl();
27 |
28 | return (
29 |
30 |
31 |
32 |
33 |
34 |
35 | App
36 |
37 |
38 |
39 |
40 |
41 | useAppState
42 |
43 |
44 |
45 | isAvailable on {platform}: {JSON.stringify(availableFeatures.appState)}{' '}
46 |
47 | state: {JSON.stringify(state)}
48 |
49 |
50 |
51 |
52 | useAppUrlOpen
53 |
54 |
55 |
56 | isAvailable on {platform}: {JSON.stringify(availableFeatures.appUrlOpen)}{' '}
57 |
58 | {availableFeatures.appUrlOpen && appUrlOpen: {appUrlOpen}
}
59 | {availableFeatures.getLaunchUrl && launchUrl: {launchUrl}
}
60 |
61 |
62 |
63 |
64 | useLaunchUrl
65 |
66 |
67 |
68 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getLaunchUrl)}{' '}
69 |
70 | {availableFeatures.getLaunchUrl && launchUrl: {launchUrl}
}
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export default AppPage;
79 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/BrowserPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Capacitor } from '@capacitor/core';
3 | import {
4 | IonContent,
5 | IonHeader,
6 | IonPage,
7 | IonTitle,
8 | IonToolbar,
9 | IonButtons,
10 | IonMenuButton,
11 | IonCard,
12 | IonCardHeader,
13 | IonCardTitle,
14 | IonCardContent,
15 | IonButton,
16 | IonInput,
17 | } from '@ionic/react';
18 |
19 | import { useClose, useOpen, availableFeatures } from '@capacitor-community/browser-react';
20 |
21 | const BrowserPage: React.FC = () => {
22 | const platform = Capacitor.getPlatform();
23 | const { close } = useClose();
24 | const { open } = useOpen();
25 | const [openUrlText, setOpenUrlText] = useState('https://www.ionicframework.com');
26 |
27 | const handleClose = () => {
28 | if (availableFeatures.close) {
29 | close();
30 | }
31 | };
32 |
33 | const handleOpen = () => {
34 | if (availableFeatures.open) {
35 | open({
36 | url: openUrlText,
37 | });
38 | }
39 | };
40 |
41 | return (
42 |
43 |
44 |
45 |
46 |
47 |
48 | Browser
49 |
50 |
51 |
52 |
53 |
54 | useClose
55 |
56 |
57 |
58 | isAvailable on {platform}: {JSON.stringify(availableFeatures.close)}{' '}
59 |
60 | {availableFeatures.close && Close }
61 |
62 |
63 |
64 |
65 | useOpen
66 |
67 |
68 |
69 | isAvailable on {platform}: {JSON.stringify(availableFeatures.open)}{' '}
70 |
71 | {availableFeatures.open && (
72 |
73 |
74 | setOpenUrlText(e.detail.value!)}
77 | />
78 |
79 |
80 | Open
81 |
82 |
83 | )}
84 |
85 |
86 |
87 |
88 | );
89 | };
90 |
91 | export default BrowserPage;
92 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/CameraPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | IonButton,
15 | } from '@ionic/react';
16 | import { Capacitor } from '@capacitor/core';
17 | import { CameraResultType } from '@capacitor/camera';
18 | import { useCamera, availableFeatures } from '@capacitor-community/camera-react';
19 |
20 | const CameraPage: React.FC = () => {
21 | const platform = Capacitor.getPlatform();
22 | const { photo, getPhoto } = useCamera();
23 |
24 | const handleTakePhoto = () => {
25 | if (availableFeatures.getPhoto) {
26 | getPhoto({
27 | quality: 100,
28 | allowEditing: false,
29 | resultType: CameraResultType.DataUrl,
30 | });
31 | }
32 | };
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 | Camera
42 |
43 |
44 |
45 |
46 |
47 | useCamera
48 |
49 |
50 |
51 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getPhoto)}{' '}
52 |
53 | {availableFeatures.getPhoto && (
54 |
55 |
56 | Take Photo
57 |
58 |
{photo &&
}
59 |
60 | )}
61 |
62 |
63 |
64 |
65 | );
66 | };
67 |
68 | export default CameraPage;
69 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/ClipboardPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | IonInput,
15 | IonButton,
16 | } from '@ionic/react';
17 | import { Capacitor } from '@capacitor/core';
18 | import { useClipboard, availableFeatures } from '@capacitor-community/clipboard-react';
19 |
20 | const ClipboardPage: React.FC = () => {
21 | const platform = Capacitor.getPlatform();
22 | const { value, getValue, setValue } = useClipboard();
23 | const [inputText, setInputText] = useState('Text to copy');
24 |
25 | const handleCopy = () => {
26 | if (availableFeatures.useClipboard) {
27 | setValue(inputText);
28 | }
29 | };
30 |
31 | const handlePaste = () => {
32 | if (availableFeatures.useClipboard) {
33 | getValue();
34 | }
35 | };
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
43 |
44 | Clipboard
45 |
46 |
47 |
48 |
49 |
50 | useClipboard
51 |
52 |
53 |
54 | isAvailable on {platform}: {JSON.stringify(availableFeatures.useClipboard)}{' '}
55 |
56 | {availableFeatures.useClipboard && (
57 | <>
58 |
59 | setInputText(e.detail.value!)} />
60 |
61 |
62 | Copy Input Text
63 |
64 |
65 | Paste Text into Div
66 |
67 | {value}
68 | >
69 | )}
70 |
71 |
72 |
73 |
74 | );
75 | };
76 |
77 | export default ClipboardPage;
78 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/DevicePage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | } from '@ionic/react';
15 | import { Capacitor } from '@capacitor/core';
16 | import {
17 | useGetInfo,
18 | useGetLanguageCode,
19 | availableFeatures,
20 | } from '@capacitor-community/device-react';
21 |
22 | const DevicePage: React.FC = () => {
23 | const platform = Capacitor.getPlatform();
24 | const { info } = useGetInfo();
25 | const { languageCode } = useGetLanguageCode();
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 | Device
35 |
36 |
37 |
38 |
39 |
40 | useGetInfo
41 |
42 |
43 |
44 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getInfo)}{' '}
45 |
46 | {availableFeatures.getInfo && (
47 | <>
48 | Device info:
49 | {JSON.stringify(info)}
50 | >
51 | )}
52 |
53 |
54 |
55 |
56 | useGetLanguageCode
57 |
58 |
59 |
60 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getLanguageCode)}{' '}
61 |
62 | {availableFeatures.getLanguageCode && (
63 | <>
64 | Language Code:
65 | {languageCode}
66 | >
67 | )}
68 |
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | export default DevicePage;
76 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/GeolocationPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | IonButton,
15 | } from '@ionic/react';
16 | import { Capacitor } from '@capacitor/core';
17 | import {
18 | useCurrentPosition,
19 | useWatchPosition,
20 | availableFeatures,
21 | } from '@capacitor-community/geolocation-react';
22 |
23 | const GeolocationPage: React.FC = () => {
24 | const platform = Capacitor.getPlatform();
25 | const { error, currentPosition, getPosition } = useCurrentPosition();
26 | const { currentPosition: watchPosition, startWatch, clearWatch } = useWatchPosition();
27 |
28 | const handleRefreshPosition = () => {
29 | if (availableFeatures.getCurrentPosition) {
30 | getPosition();
31 | }
32 | };
33 |
34 | console.log(error, currentPosition);
35 |
36 | return (
37 |
38 |
39 |
40 |
41 |
42 |
43 | Geolocation
44 |
45 |
46 |
47 |
48 |
49 | useCurrentPosition
50 |
51 |
52 |
53 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getCurrentPosition)}
54 |
55 | {availableFeatures.getCurrentPosition && (
56 | <>
57 | Current Position
58 | Lat: {currentPosition && currentPosition.coords.latitude}
59 | Lon: {currentPosition && currentPosition.coords.longitude}
60 |
61 | Refresh Current Position
62 |
63 | >
64 | )}
65 |
66 |
67 |
68 |
69 | useWatchPosition
70 |
71 |
72 |
73 | isAvailable on {platform}: {JSON.stringify(availableFeatures.watchPosition)}
74 |
75 | {availableFeatures.watchPosition && (
76 | <>
77 | Current Position
78 | Lat: {watchPosition && watchPosition.coords.latitude}
79 | Lon: {watchPosition && watchPosition.coords.longitude}
80 |
81 | startWatch()}>Start watching location
82 |
83 |
84 | clearWatch()}>Stop watching location
85 |
86 | >
87 | )}
88 |
89 |
90 |
91 |
92 | );
93 | };
94 |
95 | export default GeolocationPage;
96 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/Home.css:
--------------------------------------------------------------------------------
1 | .welcome-card img {
2 | max-height: 35vh;
3 | overflow: hidden;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IonButtons,
3 | IonCard,
4 | IonCardContent,
5 | IonCardHeader,
6 | IonCardSubtitle,
7 | IonCardTitle,
8 | IonContent,
9 | IonHeader,
10 | IonMenuButton,
11 | IonPage,
12 | IonTitle,
13 | IonToolbar,
14 | } from '@ionic/react';
15 | import React from 'react';
16 | import './Home.css';
17 |
18 | const HomePage: React.FC = () => {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 | React Hooks
27 |
28 |
29 |
30 |
31 |
32 |
33 | Welcome
34 | Welcome to React Hooks Demo
35 |
36 |
37 | View the menu to see the demos
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
45 | export default HomePage;
46 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/KeyboardPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | IonButton,
15 | IonInput,
16 | } from '@ionic/react';
17 | import { Capacitor } from '@capacitor/core';
18 | import { useKeyboard } from '@capacitor-community/keyboard-react';
19 |
20 | const KeyboardPage: React.FC = () => {
21 | const platform = Capacitor.getPlatform();
22 | const { isOpen, keyboardHeight, keyboard } = useKeyboard();
23 |
24 | useEffect(() => {
25 | keyboard.setAccessoryBarVisible({ isVisible: true });
26 | }, []);
27 |
28 | const handleOpenKeyboard = () => {
29 | keyboard.show();
30 | };
31 | const handleHideKeyboard = () => {
32 | keyboard.hide();
33 | };
34 |
35 | return (
36 |
37 |
38 |
39 |
40 |
41 |
42 | Keyboard
43 |
44 |
45 |
46 |
47 |
48 | useKeyboard
49 |
50 |
51 | {/*
52 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getPhoto)}{' '}
53 |
*/}
54 | {/* {availableFeatures.getPhoto && ( */}
55 |
56 |
57 | Open Keyboard
58 | Hide Keyboard
59 |
60 |
61 | isOpen: {isOpen ? 'true' : 'false'}, keyboard height: {keyboardHeight}
62 |
63 |
64 | Input:
65 |
66 |
67 | {/* )} */}
68 |
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | export default KeyboardPage;
76 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/NetworkPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | } from '@ionic/react';
15 | import { Capacitor } from '@capacitor/core';
16 | import { useStatus, availableFeatures } from '@capacitor-community/network-react';
17 |
18 | const NetworkPage: React.FC = () => {
19 | const platform = Capacitor.getPlatform();
20 | const { networkStatus } = useStatus();
21 |
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 | Network
30 |
31 |
32 |
33 |
34 |
35 | useStatus
36 |
37 |
38 |
39 | isAvailable on {platform}: {JSON.stringify(availableFeatures.getStatus)}{' '}
40 |
41 | {availableFeatures.getStatus && (
42 | <>
43 | Network status:
44 | {JSON.stringify(networkStatus)}
45 | >
46 | )}
47 |
48 |
49 |
50 |
51 | );
52 | };
53 |
54 | export default NetworkPage;
55 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/ScreenReaderPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Capacitor } from '@capacitor/core';
3 | import {
4 | IonContent,
5 | IonHeader,
6 | IonPage,
7 | IonTitle,
8 | IonToolbar,
9 | IonButtons,
10 | IonMenuButton,
11 | IonCard,
12 | IonCardHeader,
13 | IonCardTitle,
14 | IonCardContent,
15 | IonInput,
16 | IonButton,
17 | } from '@ionic/react';
18 | import {
19 | useIsScreenReaderEnabled,
20 | availableFeatures,
21 | useSpeak,
22 | } from '@capacitor-community/screen-reader-react';
23 |
24 | const ScreenReaderPage: React.FC = () => {
25 | const platform = Capacitor.getPlatform();
26 |
27 | const { speak } = useSpeak();
28 | const { isScreenReaderEnabled } = useIsScreenReaderEnabled();
29 | const [textToSpeak, setTextToSpeak] = useState('');
30 |
31 | const handleSpeak = () => {
32 | if (availableFeatures.speak) {
33 | speak({ value: textToSpeak });
34 | }
35 | };
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
43 |
44 | Screen Reader
45 |
46 |
47 |
48 |
49 |
50 | useIsScreenReaderEnabled
51 |
52 |
53 |
54 | isAvailable on {platform}: {JSON.stringify(availableFeatures.isScreenReaderAvailable)}{' '}
55 |
56 | {availableFeatures.isScreenReaderAvailable && (
57 | isScreenReaderEnabled: {JSON.stringify(isScreenReaderEnabled)}
58 | )}
59 |
60 |
61 |
62 |
63 |
64 | useSpeak
65 |
66 |
67 |
68 | isAvailable on {platform}: {JSON.stringify(availableFeatures.speak)}{' '}
69 |
70 | {availableFeatures.speak && (
71 |
72 |
73 | setTextToSpeak(e.detail.value!)}
76 | placeholder="Enter text to speak"
77 | />
78 |
79 |
80 | Speak
81 |
82 |
83 | )}
84 |
85 |
86 |
87 |
88 | );
89 | };
90 |
91 | export default ScreenReaderPage;
92 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/StoragePage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | IonContent,
4 | IonHeader,
5 | IonPage,
6 | IonTitle,
7 | IonToolbar,
8 | IonButtons,
9 | IonMenuButton,
10 | IonCard,
11 | IonCardHeader,
12 | IonCardTitle,
13 | IonCardContent,
14 | IonInput,
15 | IonButton,
16 | } from '@ionic/react';
17 | import { Capacitor } from '@capacitor/core';
18 | import { useStorage, useStorageItem, availableFeatures } from '@capacitor-community/storage-react';
19 |
20 | const StoragePage: React.FC = () => {
21 | const platform = Capacitor.getPlatform();
22 |
23 | const { get, set, remove, getKeys, clear } = useStorage();
24 | const [keyText, setKeyText] = useState('');
25 | const [valueText, setValueText] = useState('');
26 | const [itemValueText, setItemValueText] = useState('');
27 | const [keys, setKeys] = useState([]);
28 | const [storageValue, setStorageValue] = useState('');
29 | const [data, setData] = useStorageItem('name');
30 |
31 | const handleSet = () => {
32 | if (availableFeatures.useStorage) {
33 | set(keyText, valueText);
34 | }
35 | };
36 |
37 | const handleGet = async () => {
38 | if (availableFeatures.useStorage) {
39 | const value = await get(keyText);
40 | setStorageValue(value || '');
41 | }
42 | };
43 |
44 | const handleRemove = () => {
45 | if (availableFeatures.useStorage) {
46 | remove(keyText);
47 | }
48 | };
49 |
50 | const handleClear = () => {
51 | if (availableFeatures.useStorage) {
52 | clear();
53 | }
54 | };
55 |
56 | const handleGetKeys = () => {
57 | if (availableFeatures.useStorage) {
58 | if (availableFeatures.useStorage) {
59 | getKeys().then((result) => setKeys(result.keys));
60 | }
61 | }
62 | };
63 |
64 | const handleSetItem = () => {
65 | if (availableFeatures.useStorage) {
66 | setData(itemValueText);
67 | }
68 | };
69 |
70 | return (
71 |
72 |
73 |
74 |
75 |
76 |
77 | Storage
78 |
79 |
80 |
81 |
82 |
83 | useStorage
84 |
85 |
86 |
87 | isAvailable on {platform}: {JSON.stringify(availableFeatures.useStorage)}{' '}
88 |
89 | {availableFeatures.useStorage && (
90 | <>
91 |
92 | Key: setKeyText(e.detail.value!)} />
93 |
94 |
95 | Value:{' '}
96 | setValueText(e.detail.value!)} />
97 |
98 |
99 | Set
100 | Get
101 | Remove
102 | Clear
103 | Get Keys
104 |
105 | Value: {storageValue}
106 | Keys: {keys.map((k) => `${k}, `)}
107 | >
108 | )}
109 |
110 |
111 |
112 |
113 | useStorageItem
114 |
115 |
116 |
117 | isAvailable on {platform}: {JSON.stringify(availableFeatures.useStorage)}{' '}
118 |
119 | {availableFeatures.useStorage && (
120 | <>
121 | Key: name
122 |
123 | Value:{' '}
124 | setItemValueText(e.detail.value!)}
127 | />
128 |
129 |
130 | Set
131 |
132 | Value: {data}
133 | >
134 | )}
135 |
136 |
137 |
138 |
139 | );
140 | };
141 |
142 | export default StoragePage;
143 |
--------------------------------------------------------------------------------
/packages/test-app/src/pages/template.tsx:
--------------------------------------------------------------------------------
1 | // import React from 'react';
2 | // import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButtons, IonMenuButton, IonCard, IonCardHeader, IonCardTitle, IonCardContent } from '@ionic/react';
3 | // import { usePlatform } from '@ionic/react-hooks/platform';
4 |
5 | // const XState: React.FC = () => {
6 | // const { platform } = usePlatform();
7 |
8 | // return (
9 | //
10 | //
11 | //
12 | //
13 | //
14 | //
15 | //
16 | //
17 | //
18 | //
19 | //
20 | //
21 | //
22 | // X
23 | //
24 | //
25 | //
26 | // isAvailable on {platform}: {JSON.stringify(false)}
27 | //
28 | //
29 | //
30 | //
31 | // );
32 | // };
33 |
34 | // export default XState;
35 | export default {};
36 |
--------------------------------------------------------------------------------
/packages/test-app/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | declare namespace NodeJS {
6 | interface ProcessEnv {
7 | readonly NODE_ENV: 'development' | 'production' | 'test';
8 | readonly PUBLIC_URL: string;
9 | }
10 | }
11 |
12 | declare module '*.bmp' {
13 | const src: string;
14 | export default src;
15 | }
16 |
17 | declare module '*.gif' {
18 | const src: string;
19 | export default src;
20 | }
21 |
22 | declare module '*.jpg' {
23 | const src: string;
24 | export default src;
25 | }
26 |
27 | declare module '*.jpeg' {
28 | const src: string;
29 | export default src;
30 | }
31 |
32 | declare module '*.png' {
33 | const src: string;
34 | export default src;
35 | }
36 |
37 | declare module '*.webp' {
38 | const src: string;
39 | export default src;
40 | }
41 |
42 | declare module '*.svg' {
43 | import * as React from 'react';
44 |
45 | export const ReactComponent: React.FunctionComponent>;
46 |
47 | const src: string;
48 | export default src;
49 | }
50 |
51 | declare module '*.module.css' {
52 | const classes: { readonly [key: string]: string };
53 | export default classes;
54 | }
55 |
56 | declare module '*.module.scss' {
57 | const classes: { readonly [key: string]: string };
58 | export default classes;
59 | }
60 |
61 | declare module '*.module.sass' {
62 | const classes: { readonly [key: string]: string };
63 | export default classes;
64 | }
65 |
--------------------------------------------------------------------------------
/packages/test-app/src/theme.css:
--------------------------------------------------------------------------------
1 |
2 | /* Ionic Variables and Theming. For more information, please see
3 | // https://beta.ionicframework.com/docs/theming/
4 | // The app direction is used to include
5 | // rtl styles in your app. For more information, please see
6 | // https://beta.ionicframework.com/docs/layout/rtl
7 | // $app-direction: ltr;
8 | // Ionic Colors
9 | // --------------------------------------------------
10 | // Named colors makes it easy to reuse colors on various components.
11 | // It's highly recommended to change the default colors
12 | // to match your app's branding. Ionic provides eight layered colors
13 | // that can be changed to theme an app. Additional colors can be
14 | // added as well (see below). For more information, please see
15 | // https://beta.ionicframework.com/docs/theming/advanced
16 | // To easily create custom color palettes for your app’s UI,
17 | // check out our color generator:
18 | // https://beta.ionicframework.com/docs/theming/color-generator
19 | */
20 |
21 | :root {
22 | --ion-color-angular: #ac282b;
23 | --ion-color-communication: #8e8d93;
24 | --ion-color-tooling: #fe4c52;
25 | --ion-color-services: #fd8b2d;
26 | --ion-color-design: #fed035;
27 | --ion-color-workshop: #69bb7b;
28 | --ion-color-food: #3bc7c4;
29 | --ion-color-documentation: #b16be3;
30 | --ion-color-navigation: #6600cc;
31 |
32 | --ion-color-primary: #3880ff;
33 | --ion-color-primary-rgb: 56, 128, 255;
34 | --ion-color-primary-contrast: #ffffff;
35 | --ion-color-primary-contrast-rgb: 255, 255, 255;
36 | --ion-color-primary-shade: #3171e0;
37 | --ion-color-primary-tint: #4c8dff;
38 |
39 | --ion-color-secondary: #0cd1e8;
40 | --ion-color-secondary-rgb: 12, 209, 232;
41 | --ion-color-secondary-contrast: #ffffff;
42 | --ion-color-secondary-contrast-rgb: 255, 255, 255;
43 | --ion-color-secondary-shade: #0bb8cc;
44 | --ion-color-secondary-tint: #24d6ea;
45 |
46 | --ion-color-tertiary: #7044ff;
47 | --ion-color-tertiary-rgb: 112, 68, 255;
48 | --ion-color-tertiary-contrast: #ffffff;
49 | --ion-color-tertiary-contrast-rgb: 255, 255, 255;
50 | --ion-color-tertiary-shade: #633ce0;
51 | --ion-color-tertiary-tint: #7e57ff;
52 |
53 | --ion-color-success: #10dc60;
54 | --ion-color-success-rgb: 16, 220, 96;
55 | --ion-color-success-contrast: #ffffff;
56 | --ion-color-success-contrast-rgb: 255, 255, 255;
57 | --ion-color-success-shade: #0ec254;
58 | --ion-color-success-tint: #28e070;
59 |
60 | --ion-color-warning: #ffce00;
61 | --ion-color-warning-rgb: 255, 206, 0;
62 | --ion-color-warning-contrast: #ffffff;
63 | --ion-color-warning-contrast-rgb: 255, 255, 255;
64 | --ion-color-warning-shade: #e0b500;
65 | --ion-color-warning-tint: #ffd31a;
66 |
67 | --ion-color-danger: #f04141;
68 | --ion-color-danger-rgb: 245, 61, 61;
69 | --ion-color-danger-contrast: #ffffff;
70 | --ion-color-danger-contrast-rgb: 255, 255, 255;
71 | --ion-color-danger-shade: #d33939;
72 | --ion-color-danger-tint: #f25454;
73 |
74 | --ion-color-dark: #222428;
75 | --ion-color-dark-rgb: 34, 34, 34;
76 | --ion-color-dark-contrast: #ffffff;
77 | --ion-color-dark-contrast-rgb: 255, 255, 255;
78 | --ion-color-dark-shade: #1e2023;
79 | --ion-color-dark-tint: #383a3e;
80 |
81 | --ion-color-medium: #989aa2;
82 | --ion-color-medium-rgb: 152, 154, 162;
83 | --ion-color-medium-contrast: #ffffff;
84 | --ion-color-medium-contrast-rgb: 255, 255, 255;
85 | --ion-color-medium-shade: #86888f;
86 | --ion-color-medium-tint: #a2a4ab;
87 |
88 | --ion-color-light: #f4f5f8;
89 | --ion-color-light-rgb: 244, 244, 244;
90 | --ion-color-light-contrast: #000000;
91 | --ion-color-light-contrast-rgb: 0, 0, 0;
92 | --ion-color-light-shade: #d7d8da;
93 | --ion-color-light-tint: #f5f6f9;
94 | }
95 |
96 | /* Additional Ionic Colors
97 | // --------------------------------------------------
98 | // In order to add colors to be used with Ionic components,
99 | // the color should be added as a class with the convention `.ion-color-{COLOR}`
100 | // where `{COLOR}` is the color to be used on the Ionic component
101 | // and each variant is defined for the color. For more information, please see
102 | // https://beta.ionicframework.com/docs/theming/advanced
103 | */
104 |
105 | .ion-color-favorite {
106 | --ion-color-base: #69bb7b;
107 | --ion-color-base-rgb: 105, 187, 123;
108 | --ion-color-contrast: #ffffff;
109 | --ion-color-contrast-rgb: 255, 255, 255;
110 | --ion-color-shade: #5ca56c;
111 | --ion-color-tint: #78c288;
112 | }
113 |
114 | .ion-color-twitter {
115 | --ion-color-base: #1da1f4;
116 | --ion-color-base-rgb: 29, 161, 244;
117 | --ion-color-contrast: #ffffff;
118 | --ion-color-contrast-rgb: 255, 255, 255;
119 | --ion-color-shade: #1a8ed7;
120 | --ion-color-tint: #34aaf5;
121 | }
122 |
123 | .ion-color-google {
124 | --ion-color-base: #dc4a38;
125 | --ion-color-base-rgb: 220, 74, 56;
126 | --ion-color-contrast: #ffffff;
127 | --ion-color-contrast-rgb: 255, 255, 255;
128 | --ion-color-shade: #c24131;
129 | --ion-color-tint: #e05c4c;
130 | }
131 |
132 | .ion-color-vimeo {
133 | --ion-color-base: #23b6ea;
134 | --ion-color-base-rgb: 35, 182, 234;
135 | --ion-color-contrast: #ffffff;
136 | --ion-color-contrast-rgb: 255, 255, 255;
137 | --ion-color-shade: #1fa0ce;
138 | --ion-color-tint: #39bdec;
139 | }
140 |
141 | .ion-color-facebook {
142 | --ion-color-base: #3b5998;
143 | --ion-color-base-rgb: 59, 89, 152;
144 | --ion-color-contrast: #ffffff;
145 | --ion-color-contrast-rgb: 255, 255, 255;
146 | --ion-color-shade: #344e86;
147 | --ion-color-tint: #4f6aa2;
148 | }
149 |
150 | /* Shared Variables
151 | // --------------------------------------------------
152 | // To customize the look and feel of this app, you can override
153 | // the CSS variables found in Ionic's source files.
154 | // To view all the possible Ionic variables, see:
155 | // https://beta.ionicframework.com/docs/theming/css-variables#ionic-variables
156 | */
157 |
158 | :root {
159 | --ion-headings-font-weight: 300;
160 |
161 | --ion-color-angular: #ac282b;
162 | --ion-color-communication: #8e8d93;
163 | --ion-color-tooling: #fe4c52;
164 | --ion-color-services: #fd8b2d;
165 | --ion-color-design: #fed035;
166 | --ion-color-workshop: #69bb7b;
167 | --ion-color-food: #3bc7c4;
168 | --ion-color-documentation: #b16be3;
169 | --ion-color-navigation: #6600cc;
170 | }
171 |
172 | .md {
173 | --ion-toolbar-background: var(--ion-color-primary);
174 | --ion-toolbar-color: #fff;
175 | --ion-toolbar-color-activated: #fff;
176 | }
177 |
--------------------------------------------------------------------------------
/packages/test-app/src/theme/variables.css:
--------------------------------------------------------------------------------
1 | /* Ionic Variables and Theming. For more info, please see:
2 | http://ionicframework.com/docs/theming/ */
3 |
4 | /** Ionic CSS Variables **/
5 | :root {
6 | /** primary **/
7 | --ion-color-primary: #3880ff;
8 | --ion-color-primary-rgb: 56, 128, 255;
9 | --ion-color-primary-contrast: #ffffff;
10 | --ion-color-primary-contrast-rgb: 255, 255, 255;
11 | --ion-color-primary-shade: #3171e0;
12 | --ion-color-primary-tint: #4c8dff;
13 |
14 | /** secondary **/
15 | --ion-color-secondary: #0cd1e8;
16 | --ion-color-secondary-rgb: 12, 209, 232;
17 | --ion-color-secondary-contrast: #ffffff;
18 | --ion-color-secondary-contrast-rgb: 255, 255, 255;
19 | --ion-color-secondary-shade: #0bb8cc;
20 | --ion-color-secondary-tint: #24d6ea;
21 |
22 | /** tertiary **/
23 | --ion-color-tertiary: #7044ff;
24 | --ion-color-tertiary-rgb: 112, 68, 255;
25 | --ion-color-tertiary-contrast: #ffffff;
26 | --ion-color-tertiary-contrast-rgb: 255, 255, 255;
27 | --ion-color-tertiary-shade: #633ce0;
28 | --ion-color-tertiary-tint: #7e57ff;
29 |
30 | /** success **/
31 | --ion-color-success: #10dc60;
32 | --ion-color-success-rgb: 16, 220, 96;
33 | --ion-color-success-contrast: #ffffff;
34 | --ion-color-success-contrast-rgb: 255, 255, 255;
35 | --ion-color-success-shade: #0ec254;
36 | --ion-color-success-tint: #28e070;
37 |
38 | /** warning **/
39 | --ion-color-warning: #ffce00;
40 | --ion-color-warning-rgb: 255, 206, 0;
41 | --ion-color-warning-contrast: #ffffff;
42 | --ion-color-warning-contrast-rgb: 255, 255, 255;
43 | --ion-color-warning-shade: #e0b500;
44 | --ion-color-warning-tint: #ffd31a;
45 |
46 | /** danger **/
47 | --ion-color-danger: #f04141;
48 | --ion-color-danger-rgb: 245, 61, 61;
49 | --ion-color-danger-contrast: #ffffff;
50 | --ion-color-danger-contrast-rgb: 255, 255, 255;
51 | --ion-color-danger-shade: #d33939;
52 | --ion-color-danger-tint: #f25454;
53 |
54 | /** dark **/
55 | --ion-color-dark: #222428;
56 | --ion-color-dark-rgb: 34, 34, 34;
57 | --ion-color-dark-contrast: #ffffff;
58 | --ion-color-dark-contrast-rgb: 255, 255, 255;
59 | --ion-color-dark-shade: #1e2023;
60 | --ion-color-dark-tint: #383a3e;
61 |
62 | /** medium **/
63 | --ion-color-medium: #989aa2;
64 | --ion-color-medium-rgb: 152, 154, 162;
65 | --ion-color-medium-contrast: #ffffff;
66 | --ion-color-medium-contrast-rgb: 255, 255, 255;
67 | --ion-color-medium-shade: #86888f;
68 | --ion-color-medium-tint: #a2a4ab;
69 |
70 | /** light **/
71 | --ion-color-light: #f4f5f8;
72 | --ion-color-light-rgb: 244, 244, 244;
73 | --ion-color-light-contrast: #000000;
74 | --ion-color-light-contrast-rgb: 0, 0, 0;
75 | --ion-color-light-shade: #d7d8da;
76 | --ion-color-light-tint: #f5f6f9;
77 | }
78 |
--------------------------------------------------------------------------------
/packages/test-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react-jsx",
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/test-app/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-react"]
3 | }
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "skipLibCheck": true,
4 | "esModuleInterop": true,
5 | "strict": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "jsx": "react",
8 | "declaration": true,
9 | "experimentalDecorators": true,
10 | "noEmitHelpers": false,
11 | "importHelpers": false,
12 | "moduleResolution": "node",
13 | "lib": ["dom", "es2017"],
14 | "pretty": true,
15 | "module": "es2015",
16 | "noImplicitAny": true,
17 | "noUnusedLocals": false,
18 | "noUnusedParameters": false,
19 | "sourceMap": true,
20 | "target": "es2017"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-react"],
3 | "linterOptions": {
4 | "exclude": [
5 | "**/*.spec.ts",
6 | "**/*.spec.tsx"
7 | ]
8 | },
9 | "rules": {
10 | "no-conditional-assignment": false,
11 | "no-non-null-assertion": false,
12 | "no-unnecessary-type-assertion": false,
13 | "no-import-side-effect": false,
14 | "trailing-comma": false,
15 | "no-null-keyword": false,
16 | "no-console": false,
17 | "no-unbound-method": true,
18 | "no-floating-promises": false,
19 | "no-invalid-template-strings": true,
20 | "ban-export-const-enum": true,
21 | "only-arrow-functions": true,
22 | "strict-boolean-conditions": [false],
23 | "jsx-key": false,
24 | "jsx-self-close": false,
25 | "jsx-curly-spacing": [true, "never"],
26 | "jsx-boolean-value": [true, "never"],
27 | "jsx-no-bind": false,
28 | "jsx-no-lambda": false,
29 | "jsx-no-multiline-js": false,
30 | "jsx-wrap-multiline": false
31 | }
32 | }
33 |
--------------------------------------------------------------------------------