├── .all-contributorsrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.md │ ├── DISCUSSION.md │ ├── FEATURE_REQUEST.md │ └── MINOR_CHANGE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── example-release.yml │ ├── packages.yml │ └── preview.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── babel.config.js ├── commitlint.config.js ├── example ├── .expo-shared │ └── assets.json ├── .gitignore ├── .watchmanconfig ├── App.tsx ├── CHANGELOG.md ├── README.md ├── app.json ├── babel.config.js ├── metro.config.js ├── package.json └── src │ ├── app.tsx │ ├── assets │ ├── fonts │ │ ├── comic-sans-ms │ │ │ ├── regular-italic.ttf │ │ │ └── regular.ttf │ │ └── source-sans-pro │ │ │ ├── black-italic.ttf │ │ │ ├── black.ttf │ │ │ ├── bold-italic.ttf │ │ │ ├── bold.ttf │ │ │ ├── light-italic.ttf │ │ │ ├── light.ttf │ │ │ ├── medium-italic.ttf │ │ │ ├── medium.ttf │ │ │ ├── regular-italic.ttf │ │ │ ├── regular.ttf │ │ │ ├── thin-italic.ttf │ │ │ └── thin.ttf │ └── images │ │ ├── app-icon.png │ │ └── app-splash.png │ ├── atoms │ ├── author.tsx │ ├── example.tsx │ ├── index.ts │ ├── information.tsx │ ├── link.tsx │ ├── list.tsx │ ├── measurement.tsx │ ├── missing-permissions.tsx │ ├── page.tsx │ └── space.tsx │ ├── molecules │ ├── application │ │ ├── index.ts │ │ ├── use-application-android-install-referrer.tsx │ │ ├── use-application-android-last-update-time.tsx │ │ ├── use-application-install-time.tsx │ │ └── use-application-ios-id-for-vendor.tsx │ ├── battery │ │ ├── battery-states.ts │ │ ├── index.ts │ │ ├── use-battery-level.tsx │ │ ├── use-battery-low-power-mode.tsx │ │ ├── use-battery-state.tsx │ │ └── use-battery.tsx │ ├── brightness │ │ ├── index.ts │ │ ├── use-brightness.tsx │ │ ├── use-system-brightness-mode.tsx │ │ └── use-system-brightness.tsx │ ├── font │ │ ├── index.ts │ │ └── use-fonts.tsx │ ├── overview.tsx │ ├── permissions │ │ ├── index.ts │ │ └── use-permissions.tsx │ ├── screen-orientation │ │ ├── index.ts │ │ ├── use-screen-orientation-lock.tsx │ │ └── use-screen-orientation.tsx │ ├── sensors │ │ ├── index.ts │ │ ├── use-accelerometer.tsx │ │ ├── use-barometer.tsx │ │ ├── use-device-motion.tsx │ │ ├── use-gyroscope.tsx │ │ ├── use-magnetometer.tsx │ │ ├── use-pedometer-history.tsx │ │ └── use-pedometer.tsx │ ├── store-review │ │ ├── index.ts │ │ ├── use-store-review-has-action.tsx │ │ ├── use-store-review-is-available.tsx │ │ └── use-store-review-request.tsx │ └── web-browser │ │ ├── index.ts │ │ └── use-browsers.tsx │ └── providers │ ├── assets.tsx │ ├── molecule.tsx │ ├── navigation.tsx │ ├── theme.tsx │ └── urls.tsx ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── application │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── use-application-android-install-referrer.md │ │ ├── use-application-android-last-update-time.md │ │ ├── use-application-install-time.md │ │ └── use-application-ios-id-for-vendor.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── use-application-android-install-referrer.ts │ │ ├── use-application-android-last-update-time.ts │ │ ├── use-application-install-time.ts │ │ └── use-application-ios-id-for-vendor.ts │ ├── tests │ │ ├── use-application-android-install-referrer.test.ts │ │ ├── use-application-android-last-update-time.test.ts │ │ ├── use-application-install-time.test.ts │ │ └── use-application-ios-id-for-vendor.test.ts │ └── tsconfig.json ├── battery │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── use-battery-level.md │ │ ├── use-battery-low-power-mode.md │ │ ├── use-battery-state.md │ │ └── use-battery.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── use-battery-level.ts │ │ ├── use-battery-low-power-mode.ts │ │ ├── use-battery-state.ts │ │ └── use-battery.ts │ ├── tests │ │ ├── use-battery-level.test.ts │ │ ├── use-battery-low-power-mode.test.ts │ │ ├── use-battery-state.test.ts │ │ └── use-battery.test.ts │ └── tsconfig.json ├── brightness │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── use-brightness.md │ │ ├── use-system-brightness-mode.md │ │ └── use-system-brightness.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── use-brightness.ts │ │ ├── use-system-brightness-mode.ts │ │ └── use-system-brightness.ts │ ├── tests │ │ ├── use-brightness.test.ts │ │ ├── use-system-brightness-mode.test.ts │ │ └── use-system-brightness.test.ts │ └── tsconfig.json ├── font │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ └── use-fonts.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── use-fonts.ts │ ├── tests │ │ ├── assets │ │ │ ├── comic-sans-regular.ttf │ │ │ └── open-sans-regular.ttf │ │ └── use-fonts.test.ts │ └── tsconfig.json ├── permissions │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ └── use-permissions.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── use-permissions.ts │ ├── tests │ │ └── use-permissions.test.ts │ └── tsconfig.json ├── screen-orientation │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── use-screen-orientation-lock.md │ │ ├── use-screen-orientation-platform-lock.md │ │ └── use-screen-orientation.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── use-screen-orientation-lock.ts │ │ ├── use-screen-orientation-platform-lock.ts │ │ └── use-screen-orientation.ts │ ├── tests │ │ ├── use-screen-orientation-lock.test.ts │ │ ├── use-screen-orientation-platform-lock.test.ts │ │ └── use-screen-orientation.test.ts │ └── tsconfig.json ├── sensors │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── use-accelerometer.md │ │ ├── use-barometer.md │ │ ├── use-device-motion.md │ │ ├── use-gyroscope.md │ │ ├── use-magnetometer.md │ │ ├── use-pedometer-history.md │ │ └── use-pedometer.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── use-accelerometer.ts │ │ ├── use-barometer.ts │ │ ├── use-device-motion.ts │ │ ├── use-gyroscope.ts │ │ ├── use-magnetometer-uncalibrated.ts │ │ ├── use-magnetometer.ts │ │ ├── use-pedometer-history.ts │ │ └── use-pedometer.ts │ ├── tests │ │ ├── use-accelerometer.test.ts │ │ ├── use-barometer.test.ts │ │ ├── use-device-motion.test.ts │ │ ├── use-gyroscope.test.ts │ │ ├── use-magnetometer-uncalibrated.test.ts │ │ ├── use-magnetometer.test.ts │ │ ├── use-pedometer-history.test.ts │ │ └── use-pedometer.test.ts │ └── tsconfig.json ├── store-review │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── use-store-review-has-action.md │ │ ├── use-store-review-is-available.md │ │ └── use-store-review-request.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── use-store-review-has-action.ts │ │ ├── use-store-review-is-available.ts │ │ └── use-store-review-request.ts │ ├── tests │ │ ├── use-store-review-has-action.test.ts │ │ ├── use-store-review-is-available.test.ts │ │ └── use-store-review-request.test.ts │ └── tsconfig.json └── web-browser │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ ├── use-browsers.md │ └── use-warm-browser.md │ ├── package.json │ ├── src │ ├── index.ts │ ├── use-browsers.ts │ └── use-warm-browser.ts │ ├── tests │ ├── use-browsers.test.ts │ └── use-warm-browser.test.ts │ └── tsconfig.json ├── tsconfig.json └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "badgeTemplate": " -orange.svg?style=flat-square\" alt=\"contributors\" />", 3 | "commit": false, 4 | "contributors": [ 5 | { 6 | "login": "byCedric", 7 | "name": "Cedric van Putten", 8 | "avatar_url": "https://avatars2.githubusercontent.com/u/1203991?v=4", 9 | "profile": "https://bycedric.com", 10 | "contributions": [ 11 | "code", 12 | "doc", 13 | "example" 14 | ] 15 | }, 16 | { 17 | "login": "EvanBacon", 18 | "name": "Evan Bacon", 19 | "avatar_url": "https://avatars1.githubusercontent.com/u/9664363?v=4", 20 | "profile": "https://twitter.com/baconbrix", 21 | "contributions": [ 22 | "code", 23 | "doc" 24 | ] 25 | }, 26 | { 27 | "login": "rodw1995", 28 | "name": "Robbie op de Weegh", 29 | "avatar_url": "https://avatars1.githubusercontent.com/u/3266698?v=4", 30 | "profile": "https://github.com/rodw1995", 31 | "contributions": [ 32 | "code", 33 | "doc" 34 | ] 35 | }, 36 | { 37 | "login": "JB1905", 38 | "name": "Jakub Biesiada", 39 | "avatar_url": "https://avatars2.githubusercontent.com/u/28870390?v=4", 40 | "profile": "https://jb1905.github.io/portfolio/", 41 | "contributions": [ 42 | "bug", 43 | "code" 44 | ] 45 | } 46 | ], 47 | "contributorsPerLine": 7, 48 | "files": [ 49 | "README.md", 50 | "example/README.md" 51 | ], 52 | "imageSize": 100, 53 | "projectName": "use-expo", 54 | "projectOwner": "byCedric", 55 | "repoHost": "https://github.com", 56 | "repoType": "github", 57 | "skipCi": true 58 | } 59 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | indent_style = space 12 | trim_trailing_whitespace = false 13 | 14 | [*.yml] 15 | indent_size = 2 16 | indent_style = space 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # build files 2 | build/ 3 | coverage/ 4 | 5 | # dependencies 6 | node_modules/ 7 | 8 | # expo 9 | web-build/ 10 | web-report/ 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | plugins: [ 4 | '@typescript-eslint', 5 | 'jest', 6 | 'react-hooks', 7 | ], 8 | env: { 9 | 'jest/globals': true, 10 | node: true, 11 | }, 12 | extends: [ 13 | 'eslint:recommended', 14 | 'plugin:react/recommended', 15 | 'plugin:@typescript-eslint/recommended', 16 | ], 17 | rules: { 18 | '@typescript-eslint/explicit-function-return-type': 'off', 19 | '@typescript-eslint/explicit-member-accessibility': ['error', { accessibility: 'no-public' }], 20 | '@typescript-eslint/no-explicit-any': 'off', 21 | '@typescript-eslint/no-non-null-assertion': 'off', 22 | '@typescript-eslint/no-use-before-define': 'off', 23 | 'react-hooks/exhaustive-deps': 'warn', 24 | 'react-hooks/rules-of-hooks': 'error', 25 | 'react/no-unescaped-entities': 'off', 26 | 'react/prop-types': 'off', 27 | 28 | // fix: temporary use the "normal" indent until the typescript version rewrite is complete 29 | // see: https://github.com/typescript-eslint/typescript-eslint/milestone/1 30 | // '@typescript-eslint/indent': ['error', 'tab'], 31 | 'indent': ['error', 'tab', { SwitchCase: 1 }], 32 | }, 33 | settings: { 34 | react: { 35 | version: 'detect', 36 | }, 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | ### Description of the bug 7 | How would you shortly summarise the issue? 8 | 9 | ### To Reproduce 10 | What steps did you perform which led to this issue? 11 | 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 16 | #### Expected behavior 17 | What did you expect to have happened? 18 | 19 | #### Actual behavior 20 | What did it actually result in? 21 | 22 | ### Additional context 23 | Can you further explain the issue? E.g., information about version/environment or screenshots. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/DISCUSSION.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New discussion 3 | about: Start a discussion about a certain topic 4 | --- 5 | 6 | ### Topic and scope of discussion 7 | How would you summarise and scope the issue? 8 | 9 | ### Motivation 10 | Why should we have this discussion? 11 | 12 | ### Additional context 13 | Can you further explain the purpose of this discussion? E.g., screenshots or real-world examples. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | ### Description of the feature 7 | How would you briefly summarise the feature? 8 | 9 | ### Motivation 10 | Why does this feature should be implemented? 11 | 12 | ### Additional context 13 | Can you further explain the feature? E.g., screenshots or real-world examples. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/MINOR_CHANGE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Minor changes 3 | about: Suggest a small change, or fix, like typos 4 | --- 5 | 6 | #### Old version 7 | What did you find that should be changed? 8 | 9 | #### New version 10 | How should it look after the suggested change? 11 | 12 | ### Additional context 13 | Can you further clarify the change? E.g., link to a dictionary or real-world examples. 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Linked issue 2 | Provide the issue(s) which this pull request relates to or fixes. 3 | 4 | ### Additional context 5 | Are there things the maintainers should be aware of before merging or closing this pull request? 6 | -------------------------------------------------------------------------------- /.github/workflows/example-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Example Release 3 | on: 4 | push: 5 | paths: 6 | - 'example/**' 7 | branches: 8 | - master 9 | jobs: 10 | publish: 11 | name: Install and publish 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Setup repository 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: 14.x 21 | 22 | - name: Setup Expo 23 | uses: expo/expo-github-action@v5 24 | with: 25 | expo-version: 3.x 26 | expo-token: ${{ secrets.EXPO_TOKEN }} 27 | expo-cache: true 28 | 29 | - name: Find Yarn Cache 30 | id: yarn-cache-path 31 | run: echo "::set-output name=dir::$(yarn cache dir)" 32 | 33 | - name: Cache Yarn 34 | uses: actions/cache@v2 35 | with: 36 | path: ${{ steps.yarn-cache-path.outputs.dir }} 37 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 38 | restore-keys: | 39 | ${{ runner.os }}-yarn- 40 | 41 | - name: Install dependencies 42 | run: yarn --frozen-lockfile 43 | 44 | - name: Build packages 45 | run: yarn build 46 | 47 | - name: Publish app 48 | run: expo publish example 49 | env: 50 | EXPO_USE_DEV_SERVER: true 51 | -------------------------------------------------------------------------------- /.github/workflows/packages.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Packages 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - 'example/**' 7 | jobs: 8 | ci: 9 | name: Install, test and lint 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node: [10, 12, 14] 14 | steps: 15 | - name: Setup repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup Node 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: ${{ matrix.node }} 22 | 23 | - name: Find Yarn Cache 24 | id: yarn-cache-path 25 | run: echo "::set-output name=dir::$(yarn cache dir)" 26 | 27 | - name: Cache Yarn 28 | uses: actions/cache@v2 29 | with: 30 | path: ${{ steps.yarn-cache-path.outputs.dir }} 31 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 32 | restore-keys: | 33 | ${{ runner.os }}-yarn- 34 | 35 | - name: Install dependencies 36 | run: yarn install --frozen-lockfile 37 | 38 | - name: Build packages 39 | run: yarn build 40 | 41 | - name: Lint packages 42 | run: yarn lint 43 | 44 | - name: Test packages 45 | run: yarn test 46 | -------------------------------------------------------------------------------- /.github/workflows/preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Preview 3 | on: [pull_request] 4 | jobs: 5 | preview: 6 | name: Publish preview 7 | runs-on: ubuntu-latest 8 | if: github.event.pull_request.head.repo.full_name == 'bycedric/use-expo' 9 | steps: 10 | - name: Setup repository 11 | uses: actions/checkout@v2 12 | 13 | - name: Setup Node 14 | uses: actions/setup-node@v1 15 | with: 16 | node-version: 14.x 17 | 18 | - name: Setup Expo 19 | uses: expo/expo-github-action@v5 20 | with: 21 | expo-version: 3.x 22 | expo-token: ${{ secrets.EXPO_TOKEN }} 23 | expo-cache: true 24 | 25 | - name: Find Yarn Cache 26 | id: yarn-cache-path 27 | run: echo "::set-output name=dir::$(yarn cache dir)" 28 | 29 | - name: Cache Yarn 30 | uses: actions/cache@v2 31 | with: 32 | path: ${{ steps.yarn-cache-path.outputs.dir }} 33 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 34 | restore-keys: | 35 | ${{ runner.os }}-yarn- 36 | 37 | - name: Install dependencies 38 | run: yarn install --frozen-lockfile 39 | 40 | - name: Build packages 41 | run: yarn build 42 | 43 | - name: Publish preview 44 | run: expo publish example --release-channel=pr-${{ github.event.number }} 45 | env: 46 | EXPO_USE_DEV_SERVER: true 47 | 48 | - name: Get deployment link 49 | run: echo "::set-output name=path::@bycedric/use-expo?release-channel=pr-${{ github.event.number }}" 50 | id: expo 51 | 52 | - name: Comment deployment link 53 | uses: unsplash/comment-on-pr@master 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | with: 57 | msg: A preview of this PR is deployed! You can [preview the app here](https://expo.io/${{ steps.expo.outputs.path }}).

58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build files 2 | build 3 | coverage 4 | tsconfig.*tsbuildinfo 5 | 6 | # dependencies 7 | node_modules 8 | npm-debug.log* 9 | lerna-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | yarn.lock 13 | package-lock.json 14 | !/yarn.lock 15 | 16 | # editors 17 | .vscode 18 | .idea 19 | 20 | # expo 21 | .expo 22 | *.jks 23 | *.p12 24 | *.key 25 | *.mobileprovision 26 | *.orig.* 27 | web-build 28 | web-report 29 | 30 | # misc 31 | .DS_Store 32 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "jsxBracketSameLine": true, 6 | "trailingComma": "es5", 7 | "arrowParens": "avoid" 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Cedric van Putten 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "b89fc150b8d111f6b70ab9212c0fbf6963f5b096c04dffe22a8daf618b5703a9": true, 3 | "18244b44c8ddffdbaeb5208a7a3698042f9fa5022229e292eac1af3ee25252a1": true 4 | } 5 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p12 6 | *.key 7 | *.mobileprovision 8 | *.orig.* 9 | web-build/ 10 | web-report/ 11 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | export { App as default } from './src/app'; 2 | -------------------------------------------------------------------------------- /example/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **example:** use orientation lock names in example ([#188](https://github.com/bycedric/use-expo/issues/188)) ([49858b8](https://github.com/bycedric/use-expo/commit/49858b8aea21ad940dd7f89bc1d47e505230c6ad)) 12 | * **web-browser:** add tests, fix lint errors and finish docs ([#187](https://github.com/bycedric/use-expo/issues/187)) ([3ddd739](https://github.com/bycedric/use-expo/commit/3ddd73901a1cd2e33e0d7d6c067dd0d88b71f1cf)) 13 | 14 | 15 | ### Code Refactoring 16 | 17 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 18 | 19 | 20 | ### BREAKING CHANGES 21 | 22 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 23 | 24 | * refactor(battery): upgrade to expo sdk 37 25 | 26 | * refactor(brightness): upgrade to expo sdk 37 27 | 28 | * refactor(font): upgrade to expo sdk 37 29 | 30 | * refactor(permissions): upgrade to expo sdk 37 31 | 32 | * refactor(sensors): upgrade to expo sdk 37 33 | 34 | * refactor: upgrade monorepo to expo sdk 37 35 | 36 | * fix: use compatible semver range for react types 37 | 38 | 39 | 40 | 41 | 42 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * **example:** add example navigation listener unmount ([fdcf98d](https://github.com/bycedric/use-expo/commit/fdcf98d4935bc68015230f55f01c36e648094fdb)) 48 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 |
2 |

expo hooks

3 |

Complementary hooks for Expo

4 | 5 | 6 | 7 | contributors 8 | 9 | 10 | 11 | builds 12 | 13 | 14 | demo 15 | 16 | 17 |
18 |

19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
25 | 26 | > See [`README.md`](https://github.com/bycedric/use-expo) in root. 27 | 28 |
29 |
30 | with :heart: byCedric 31 |
32 |
33 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo hooks", 4 | "description": "complementary hooks for expo", 5 | "version": "0.0.0", 6 | "slug": "use-expo", 7 | "privacy": "public", 8 | "githubUrl": "https://github.com/bycedric/use-expo", 9 | "platforms": [ 10 | "ios", 11 | "android", 12 | "web" 13 | ], 14 | "icon": "./src/assets/images/app-icon.png", 15 | "splash": { 16 | "image": "./src/assets/images/app-splash.png", 17 | "resizeMode": "contain", 18 | "backgroundColor": "#ffffff" 19 | }, 20 | "updates": { 21 | "fallbackToCacheTimeout": 0 22 | }, 23 | "assetBundlePatterns": [ 24 | "src/assets/**/*" 25 | ], 26 | "ios": { 27 | "supportsTablet": true 28 | }, 29 | "packagerOpts": { 30 | "config": "metro.config.js" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | env: { 6 | production: { 7 | plugins: ['react-native-paper/babel'], 8 | }, 9 | }, 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const { createMetroConfiguration } = require('expo-yarn-workspaces'); 3 | 4 | module.exports = createMetroConfiguration(__dirname); 5 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@use-expo/example", 4 | "version": "2.0.0", 5 | "license": "MIT", 6 | "author": "Cedric van Putten (https://bycedric.com)", 7 | "homepage": "https://github.com/bycedric/use-expo/tree/master/example#readme", 8 | "main": ".expo/AppEntry.js", 9 | "bugs": { 10 | "url": "https://github.com/bycedric/use-expo/issues" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/bycedric/use-expo.git", 15 | "directory": "example" 16 | }, 17 | "scripts": { 18 | "postinstall": "expo-yarn-workspaces postinstall", 19 | "start": "expo start", 20 | "android": "expo start --android", 21 | "ios": "expo start --ios", 22 | "web": "expo start --web" 23 | }, 24 | "dependencies": { 25 | "expo": "^37.0.0", 26 | "expo-battery": "~2.2.1", 27 | "expo-brightness": "~8.1.0", 28 | "expo-camera": "~8.2.0", 29 | "expo-constants": "~9.0.0", 30 | "expo-font": "~8.1.0", 31 | "expo-keep-awake": "~8.1.0", 32 | "expo-permissions": "~8.1.0", 33 | "expo-screen-orientation": "~1.0.0", 34 | "expo-sensors": "~8.1.0", 35 | "expo-web-browser": "~8.2.1", 36 | "expo-yarn-workspaces": "^1.2.1", 37 | "lodash": "^4.17.15", 38 | "react": "16.9.0", 39 | "react-dom": "16.9.0", 40 | "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.0.tar.gz", 41 | "react-native-gesture-handler": "~1.6.0", 42 | "react-native-paper": "^2.16.0", 43 | "react-native-reanimated": "~1.7.0", 44 | "react-native-screens": "~2.2.0", 45 | "react-native-web": "^0.11.7", 46 | "react-navigation": "^4.0.10", 47 | "react-navigation-stack": "^1.10.3" 48 | }, 49 | "devDependencies": { 50 | "@expo/webpack-config": "^0.12.3", 51 | "@types/lodash": "^4.14.149", 52 | "@types/react": "^16.9.11", 53 | "@types/react-native": "^0.60.22", 54 | "babel-preset-expo": "^8.1.0", 55 | "typescript": "^3.8.3" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/src/app.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { AssetsProvider } from './providers/assets'; 3 | import { NavigationProvider } from './providers/navigation'; 4 | import { ThemeProvider } from './providers/theme'; 5 | 6 | export const App: React.SFC = () => ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /example/src/assets/fonts/comic-sans-ms/regular-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/comic-sans-ms/regular-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/comic-sans-ms/regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/comic-sans-ms/regular.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/black-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/black-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/black.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/bold-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/bold-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/bold.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/light-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/light-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/light.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/medium-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/medium-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/medium.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/regular-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/regular-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/regular.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/thin-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/thin-italic.ttf -------------------------------------------------------------------------------- /example/src/assets/fonts/source-sans-pro/thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/fonts/source-sans-pro/thin.ttf -------------------------------------------------------------------------------- /example/src/assets/images/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/images/app-icon.png -------------------------------------------------------------------------------- /example/src/assets/images/app-splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/example/src/assets/images/app-splash.png -------------------------------------------------------------------------------- /example/src/atoms/author.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TouchableOpacity } from 'react-native'; 3 | import { Text } from 'react-native-paper'; 4 | import { Linking } from 'expo'; 5 | import { Link } from './link'; 6 | import { Space } from './space'; 7 | import { author } from '../providers/urls'; 8 | 9 | export const Author: React.SFC = () => ( 10 | Linking.openURL(author())}> 11 | 12 | 13 | with ❤️ byCedric 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /example/src/atoms/example.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { withNavigation, NavigationInjectedProps } from 'react-navigation'; 4 | import { Space, SpaceDimension } from './space'; 5 | 6 | export const ExampleComponent: React.SFC = (props) => { 7 | const [ready, setReady] = useState(false); 8 | 9 | useEffect(() => { 10 | const subscription = props.navigation.addListener( 11 | 'didFocus', 12 | () => { 13 | subscription.remove(); 14 | setReady(true); 15 | } 16 | ); 17 | 18 | return subscription.remove; 19 | }, [props.navigation]); 20 | 21 | if (!ready) return null; 22 | 23 | return ( 24 | 25 | {props.children} 26 | 27 | ); 28 | }; 29 | 30 | export const Example = withNavigation(ExampleComponent); 31 | 32 | export interface ExampleProps { 33 | space?: SpaceDimension; 34 | } 35 | 36 | Example.defaultProps = { 37 | space: 'large', 38 | }; 39 | 40 | const styles = StyleSheet.create({ 41 | content: { 42 | flex: 1, 43 | alignItems: 'center', 44 | justifyContent: 'center', 45 | backgroundColor: '#f2f2f2', 46 | borderRadius: 5, 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /example/src/atoms/index.ts: -------------------------------------------------------------------------------- 1 | export * from './author'; 2 | export * from './example'; 3 | export * from './information'; 4 | export * from './link'; 5 | export * from './list'; 6 | export * from './measurement'; 7 | export * from './missing-permissions'; 8 | export * from './page'; 9 | export * from './space'; 10 | -------------------------------------------------------------------------------- /example/src/atoms/information.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Paragraph } from 'react-native-paper'; 3 | import { Space } from './space'; 4 | 5 | export const Information: React.SFC = (props) => ( 6 | 7 | {props.children} 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /example/src/atoms/link.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { Text } from 'react-native-paper'; 4 | import { Linking } from 'expo'; 5 | 6 | export const Link: React.SFC = (props) => { 7 | const onPress = props.url 8 | ? () => Linking.openURL(props.url!) 9 | : undefined; 10 | 11 | return ( 12 | 13 | {props.children} 14 | 15 | ); 16 | }; 17 | 18 | interface LinkProps { 19 | url?: string; 20 | } 21 | 22 | const styles = StyleSheet.create({ 23 | link: { 24 | fontFamily: 'source-sans-pro-medium', 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /example/src/atoms/list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withNavigation, NavigationInjectedProps } from 'react-navigation'; 3 | import { View, StyleSheet, TouchableOpacity } from 'react-native'; 4 | import { Text, Title, Paragraph } from 'react-native-paper'; 5 | import { Link } from './link'; 6 | import { Space } from './space'; 7 | 8 | export const ListHeader: React.SFC = (props) => ( 9 | 10 | {props.children} 11 | 12 | ); 13 | 14 | const ListItemComponent: React.SFC = (props) => ( 15 | props.navigation.navigate(props.route || props.name)}> 16 | 17 | 18 | 19 | {props.name} 20 | {props.description} 21 | 22 | 23 | 24 | ); 25 | 26 | export const ListItem = withNavigation(ListItemComponent); 27 | 28 | export interface ListItemProps { 29 | name: string; 30 | description: React.ReactNode; 31 | route?: string; 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | item: { 36 | flex: 0, 37 | flexDirection: 'row', 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /example/src/atoms/measurement.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { round } from 'lodash'; 4 | 5 | export const Measurement: React.SFC = (props) => { 6 | const name = props.name ? `${props.name} — ` : ''; 7 | const value = typeof props.value === 'number' 8 | ? format(props.value, props.precision) 9 | : props.value; 10 | 11 | const unit = props.unit 12 | ? {props.unit} 13 | : null; 14 | 15 | return ( 16 | {name}{value}{unit} 17 | ); 18 | }; 19 | 20 | interface MeasurementProps { 21 | name?: string; 22 | value: number | string; 23 | precision?: number; 24 | unit?: string; 25 | } 26 | 27 | const format = (value: number, precision = 3) => { 28 | const rounded = round(value, precision).toFixed(precision); 29 | 30 | return value >= 0 ? ` ${rounded}` : rounded; 31 | }; 32 | -------------------------------------------------------------------------------- /example/src/atoms/missing-permissions.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Linking } from 'react-native'; 3 | import { Button, Caption } from 'react-native-paper'; 4 | import { Space } from './space'; 5 | 6 | export const MissingPermissions: React.SFC = (props) => ( 7 | <> 8 | 9 | {props.children} 10 | 11 | 12 | {(props.canConfirm && props.onConfirm) 13 | ? 14 | : 15 | } 16 | 17 | ); 18 | 19 | export interface MissingPermissionsProps { 20 | /** Some information which permissions and why they are necessary. */ 21 | children: React.ReactNode; 22 | /** Determine if a user can give the permission using the `onConfirm` callback (defaults to `true`) */ 23 | canConfirm?: boolean; 24 | /** A callback invoked when the user wants to grant the permission(s), if possible */ 25 | onConfirm?: () => any; 26 | } 27 | 28 | MissingPermissions.defaultProps = { 29 | canConfirm: true, 30 | }; 31 | -------------------------------------------------------------------------------- /example/src/atoms/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, ScrollView, StyleSheet } from 'react-native'; 3 | import { Title, Paragraph } from 'react-native-paper'; 4 | import { Author } from './author'; 5 | import { Space } from './space'; 6 | 7 | export const Page: React.SFC = (props) => ( 8 | 9 | {props.header} 10 | 11 | 12 | {props.title} 13 | 14 | 15 | {props.subtitle} 16 | 17 | 18 | 19 | {props.children} 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | 27 | export interface PageProps { 28 | header?: React.ReactNode; 29 | title: React.ReactNode; 30 | subtitle: React.ReactNode; 31 | } 32 | 33 | const styles = StyleSheet.create({ 34 | container: { 35 | flexGrow: 1, 36 | }, 37 | body: { 38 | flexGrow: 1, 39 | }, 40 | footer: { 41 | alignItems: 'center', 42 | }, 43 | title: { 44 | textAlign: 'center', 45 | fontSize: 32, 46 | }, 47 | subtitle: { 48 | textAlign: 'center', 49 | fontSize: 16, 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /example/src/molecules/application/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-application-install-time'; 2 | export * from './use-application-android-install-referrer'; 3 | export * from './use-application-android-last-update-time'; 4 | export * from './use-application-ios-id-for-vendor'; 5 | -------------------------------------------------------------------------------- /example/src/molecules/application/use-application-android-install-referrer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useApplicationAndroidInstallReferrer } from '@use-expo/application'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseApplicationAndroidInstallReferrer: React.SFC = (props) => { 9 | const [installReferrer] = useApplicationAndroidInstallReferrer(); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the install referrer with the Application module. 18 | 19 | 20 | App install referrer: 21 | {installReferrer} 22 | 23 | 24 | ); 25 | }; 26 | 27 | UseApplicationAndroidInstallReferrer.defaultProps = { 28 | name: 'useApplicationAndroidInstallReferrer', 29 | description: 'get the referrer URL of the installed app', 30 | }; 31 | -------------------------------------------------------------------------------- /example/src/molecules/application/use-application-android-last-update-time.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useApplicationAndroidLastUpdateTime } from '@use-expo/application'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseApplicationAndroidLastUpdateTime: React.SFC = (props) => { 9 | const [lastUpdateTime] = useApplicationAndroidLastUpdateTime(); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the last update time from Google Play, with the Application module. 18 | 19 | 20 | App installed at: 21 | {!lastUpdateTime 22 | ? - 23 | : <> 24 | {lastUpdateTime.toDateString()} 25 | {lastUpdateTime.toTimeString()} 26 | 27 | } 28 | 29 | 30 | ); 31 | }; 32 | 33 | UseApplicationAndroidLastUpdateTime.defaultProps = { 34 | name: 'useApplicationAndroidLastUpdateTime', 35 | description: 'get the time the app was last updated via the Google Play Store', 36 | }; 37 | -------------------------------------------------------------------------------- /example/src/molecules/application/use-application-install-time.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useApplicationInstallTime } from '@use-expo/application'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseApplicationInstallTime: React.SFC = (props) => { 9 | const [installTime] = useApplicationInstallTime(); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the date when the app was installed with the Application module. 18 | 19 | 20 | App installed at: 21 | {!installTime 22 | ? - 23 | : <> 24 | {installTime.toDateString()} 25 | {installTime.toTimeString()} 26 | 27 | } 28 | 29 | 30 | ); 31 | }; 32 | 33 | UseApplicationInstallTime.defaultProps = { 34 | name: 'useApplicationInstallTime', 35 | description: 'get the time the app was installed on the device', 36 | }; 37 | -------------------------------------------------------------------------------- /example/src/molecules/application/use-application-ios-id-for-vendor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useApplicationIosIdForVendor } from '@use-expo/application'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseApplicationIosIdForVendor: React.SFC = (props) => { 9 | const [vendorId] = useApplicationIosIdForVendor(); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the iOS vendor identifier (IDFV) with the Application module. 18 | 19 | 20 | App vendor ID: 21 | {!vendorId 22 | ? - 23 | : {vendorId} 24 | } 25 | 26 | 27 | ); 28 | }; 29 | 30 | UseApplicationIosIdForVendor.defaultProps = { 31 | name: 'useApplicationIosIdForVendor', 32 | description: 'get the time the app was installed on the device', 33 | }; 34 | -------------------------------------------------------------------------------- /example/src/molecules/battery/battery-states.ts: -------------------------------------------------------------------------------- 1 | import { BatteryState } from 'expo-battery'; 2 | 3 | export const batteryStates = { 4 | [BatteryState.UNKNOWN]: 'unknown', 5 | [BatteryState.CHARGING]: 'charging', 6 | [BatteryState.FULL]: 'full', 7 | [BatteryState.UNPLUGGED]: 'unplugged', 8 | }; 9 | -------------------------------------------------------------------------------- /example/src/molecules/battery/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-battery'; 2 | export * from './use-battery-level'; 3 | export * from './use-battery-low-power-mode'; 4 | export * from './use-battery-state'; 5 | -------------------------------------------------------------------------------- /example/src/molecules/battery/use-battery-level.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { useBatteryLevel } from '@use-expo/battery'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseBatteryLevel: React.SFC = (props) => { 9 | const [batteryLevel] = useBatteryLevel(); 10 | 11 | return ( 12 | 16 | 17 | This example only uses the Battery module. 18 | It renders the current battery level, received from the listener. 19 | 20 | 21 | Battery level 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | UseBatteryLevel.defaultProps = { 29 | name: 'useBatteryLevel', 30 | description: 'get and/or listen to the battery level', 31 | }; 32 | -------------------------------------------------------------------------------- /example/src/molecules/battery/use-battery-low-power-mode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useBatteryLowPowerMode } from '@use-expo/battery'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseBatteryLowPowerMode: React.SFC = (props) => { 9 | const [isLowPowerMode] = useBatteryLowPowerMode(); 10 | 11 | return ( 12 | 16 | 17 | This example only uses the Battery module. 18 | It renders the current battery low power mode, received from the listener. 19 | 20 | 21 | Battery low power mode? 22 | {isLowPowerMode ? 'yes' : 'no'} 23 | 24 | 25 | ); 26 | }; 27 | 28 | UseBatteryLowPowerMode.defaultProps = { 29 | name: 'useBatteryLowPowerMode', 30 | description: 'get and/or listen to the battery low power mode', 31 | }; 32 | -------------------------------------------------------------------------------- /example/src/molecules/battery/use-battery-state.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useBatteryState } from '@use-expo/battery'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | import { batteryStates } from './battery-states'; 8 | import { BatteryState } from 'expo-battery'; 9 | 10 | export const UseBatteryState: React.SFC = (props) => { 11 | const [batteryState] = useBatteryState(); 12 | 13 | return ( 14 | 18 | 19 | This example only uses the Battery module. 20 | It renders the current battery state, received from the listener. 21 | 22 | 23 | Battery state 24 | {batteryStates[batteryState || BatteryState.UNKNOWN]} 25 | 26 | 27 | ); 28 | }; 29 | 30 | UseBatteryState.defaultProps = { 31 | name: 'useBatterySate', 32 | description: 'get and/or listen to the battery state', 33 | }; 34 | -------------------------------------------------------------------------------- /example/src/molecules/battery/use-battery.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Caption } from 'react-native-paper'; 3 | import { useBattery } from '@use-expo/battery'; 4 | import { Example, Information, Link, Measurement, Page, Space } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | import { batteryStates } from './battery-states'; 8 | 9 | export const UseBattery: React.SFC = (props) => { 10 | const [battery, getBattery] = useBattery(); 11 | 12 | return ( 13 | 17 | 18 | This example only uses the Battery module. 19 | It renders the battery information it received. 20 | Note that this isn't "live" but can be updated using a callback. 21 | 22 | 23 | Battery 24 | {!!battery && ( 25 | <> 26 | 27 | 28 | 29 | 30 | )} 31 | 32 | 35 | 36 | 37 | 38 | ); 39 | }; 40 | 41 | UseBattery.defaultProps = { 42 | name: 'useBattery', 43 | description: 'get the battery level, state and power mode', 44 | }; 45 | -------------------------------------------------------------------------------- /example/src/molecules/brightness/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-brightness'; 2 | export * from './use-system-brightness'; 3 | export * from './use-system-brightness-mode'; 4 | -------------------------------------------------------------------------------- /example/src/molecules/brightness/use-brightness.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { Slider } from 'react-native'; 3 | import { Caption } from 'react-native-paper'; 4 | import { debounce, round } from 'lodash'; 5 | import { useBrightness } from '@use-expo/brightness'; 6 | import { Example, Information, Link, Page } from '../../atoms'; 7 | import { MoleculeProps } from '../../providers/molecule'; 8 | import { docs } from '../../providers/urls'; 9 | 10 | export const UseBrightness: React.SFC = props => { 11 | const [brightness, setBrightness] = useBrightness(); 12 | const setBrightnessDebounced = useMemo( 13 | () => debounce(setBrightness, 100), 14 | [setBrightness], 15 | ); 16 | 17 | return ( 18 | 19 | 20 | This example only uses the Brightness module. It renders the current screen 21 | brightness and a slider to change that. 22 | 23 | 24 | {round(Number(brightness) * 100, 1).toFixed(1)}% 25 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | UseBrightness.defaultProps = { 41 | name: 'useBrightness', 42 | description: 'change the screen brightness', 43 | }; 44 | -------------------------------------------------------------------------------- /example/src/molecules/brightness/use-system-brightness-mode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from 'react-native-paper'; 3 | import { SYSTEM_BRIGHTNESS } from 'expo-permissions'; 4 | import { BrightnessMode } from 'expo-brightness'; 5 | import { useSystemBrightnessMode } from '@use-expo/brightness'; 6 | import { usePermissions } from '@use-expo/permissions' 7 | import { Example, Information, Link, Page, Space, MissingPermissions } from '../../atoms'; 8 | import { MoleculeProps } from '../../providers/molecule'; 9 | import * as url from '../../providers/urls'; 10 | 11 | export const UseSystemBrightnessMode: React.SFC = (props) => { 12 | const [permission, askPermission] = usePermissions(SYSTEM_BRIGHTNESS); 13 | const [mode, setMode] = useSystemBrightnessMode(); 14 | 15 | return ( 16 | 20 | 21 | This example uses both the Permissions and Brightness modules. 22 | When you grant the SYSTEM_BRIGHTNESS permission, it renders two buttons to switch the system brightness mode. 23 | 24 | 25 | {(permission && permission.status !== 'granted') && ( 26 | 27 | We need permission to modify the system brightness. 28 | 29 | )} 30 | {(permission && permission.status === 'granted') && ( 31 | <> 32 | 33 | 40 | 41 | 42 | 49 | 50 | 51 | )} 52 | 53 | 54 | ); 55 | }; 56 | 57 | UseSystemBrightnessMode.defaultProps = { 58 | name: 'useSystemBrightnessMode', 59 | description: 'change the system brightness mode', 60 | } 61 | -------------------------------------------------------------------------------- /example/src/molecules/brightness/use-system-brightness.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { Slider } from 'react-native'; 3 | import { Caption } from 'react-native-paper'; 4 | import { SYSTEM_BRIGHTNESS } from 'expo-permissions'; 5 | import { useSystemBrightness } from '@use-expo/brightness'; 6 | import { usePermissions } from '@use-expo/permissions'; 7 | import { debounce, round } from 'lodash'; 8 | import { Example, Information, Link, Page, MissingPermissions } from '../../atoms'; 9 | import { MoleculeProps } from '../../providers/molecule'; 10 | import { docs } from '../../providers/urls'; 11 | 12 | export const UseSystemBrightness: React.SFC = props => { 13 | const [permission, askPermission] = usePermissions(SYSTEM_BRIGHTNESS); 14 | const [brightness, setBrightness] = useSystemBrightness(); 15 | const setBrightnessDebounced = useMemo( 16 | () => debounce(setBrightness, 100), 17 | [setBrightness], 18 | ); 19 | 20 | return ( 21 | 22 | 23 | This is example uses both the Brightness and{' '} 24 | Permissions modules. When you grant the{' '} 25 | SYSTEM_BRIGHTNESS permission, it renders a slider to change the system 26 | brightness. 27 | 28 | 29 | {permission && permission.status !== 'granted' && ( 30 | 31 | We need permission to modify the system brightness. 32 | 33 | )} 34 | {permission && permission.status === 'granted' && ( 35 | <> 36 | {round(Number(brightness) * 100, 1).toFixed(1)}% 37 | 47 | 48 | )} 49 | 50 | 51 | ); 52 | }; 53 | 54 | UseSystemBrightness.defaultProps = { 55 | name: 'useSystemBrightness', 56 | description: 'change the system brightness', 57 | }; 58 | -------------------------------------------------------------------------------- /example/src/molecules/font/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fonts'; 2 | -------------------------------------------------------------------------------- /example/src/molecules/font/use-fonts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useFonts } from '@use-expo/font'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseFonts: React.SFC = (props) => { 9 | const [loaded] = useFonts({ 10 | ComicSans: require('../../assets/fonts/comic-sans-ms/regular.ttf'), 11 | }); 12 | 13 | return ( 14 | 18 | 19 | This example uses the Font module. 20 | It loads the custom font and renders some text after it finished loading. 21 | 22 | 23 | {!loaded && Loading font...} 24 | {loaded && ( 25 | 26 | Bow down for the almighty Comic Sans! 27 | 28 | )} 29 | 30 | 31 | ); 32 | }; 33 | 34 | UseFonts.defaultProps = { 35 | name: 'useFonts', 36 | description: 'load a map of fonts', 37 | } 38 | -------------------------------------------------------------------------------- /example/src/molecules/overview.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { Header } from 'react-navigation-stack'; 3 | import Constants from 'expo-constants'; 4 | import { startCase } from 'lodash'; 5 | import { Link, ListHeader, ListItem, Page, Space } from '../atoms'; 6 | import { repo } from '../providers/urls'; 7 | 8 | import * as Application from './application'; 9 | import * as Battery from './battery'; 10 | import * as Brightness from './brightness'; 11 | import * as Font from './font'; 12 | import * as Permissions from './permissions'; 13 | import * as ScreenOrientation from './screen-orientation'; 14 | import * as Sensors from './sensors'; 15 | import * as StoreReview from './store-review'; 16 | import * as WebBrowser from './web-browser'; 17 | 18 | export const molecules = { 19 | Application, 20 | Battery, 21 | Brightness, 22 | Font, 23 | Permissions, 24 | ScreenOrientation, 25 | Sensors, 26 | StoreReview, 27 | WebBrowser, 28 | }; 29 | 30 | export const Overview: React.SFC = () => ( 31 | Complementary hooks for Expo} 34 | header={} 35 | > 36 | {Object.entries(molecules).map(([moleculeName, molecule]) => ( 37 | 38 | {startCase(moleculeName)} 39 | {Object.values(molecule).map((screen) => ( 40 | 45 | ))} 46 | 47 | ))} 48 | 49 | ); 50 | -------------------------------------------------------------------------------- /example/src/molecules/permissions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-permissions'; 2 | -------------------------------------------------------------------------------- /example/src/molecules/permissions/use-permissions.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { Caption } from 'react-native-paper'; 4 | import { Camera, Constants as CameraConstants } from 'expo-camera'; 5 | import { CAMERA } from 'expo-permissions'; 6 | import { usePermissions } from '@use-expo/permissions'; 7 | import { Example, Information, Link, Page, Space, MissingPermissions } from '../../atoms'; 8 | import { MoleculeProps } from '../../providers/molecule'; 9 | import * as url from '../../providers/urls'; 10 | 11 | export const UsePermissions: React.SFC = (props) => { 12 | const [permission, askPermission] = usePermissions(CAMERA); 13 | 14 | return ( 15 | 19 | 20 | Here you can see an example using both the Permissions and Camera modules. 21 | When you grant the CAMERA permission, it renders a simple camera with a text overlay. 22 | 23 | 24 | {(permission?.status !== 'granted') && ( 25 | 26 | We need permission to use the camera. 27 | 28 | )} 29 | {(permission?.status === 'granted') && ( 30 | 34 | 35 | 36 | ^ most handsome in the world 37 | 38 | 39 | 40 | )} 41 | 42 | 43 | ); 44 | }; 45 | 46 | UsePermissions.defaultProps = { 47 | name: 'usePermissions', 48 | description: 'get or ask permissions', 49 | }; 50 | 51 | const styles = StyleSheet.create({ 52 | camera: { 53 | flex: 1, 54 | width: '100%', 55 | alignItems: 'center', 56 | justifyContent: 'flex-end', 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /example/src/molecules/screen-orientation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-screen-orientation'; 2 | export * from './use-screen-orientation-lock'; 3 | -------------------------------------------------------------------------------- /example/src/molecules/screen-orientation/use-screen-orientation-lock.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button } from 'react-native-paper'; 3 | import { OrientationLock } from 'expo-screen-orientation'; 4 | import { useScreenOrientationLock } from '@use-expo/screen-orientation'; 5 | import { Example, Information, Link, Page, Space } from '../../atoms'; 6 | import { MoleculeProps } from '../../providers/molecule'; 7 | import { docs } from '../../providers/urls'; 8 | 9 | const orientations: OrientationLock[] = [ 10 | OrientationLock.ALL, 11 | OrientationLock.LANDSCAPE, 12 | OrientationLock.LANDSCAPE_LEFT, 13 | OrientationLock.LANDSCAPE_RIGHT, 14 | OrientationLock.PORTRAIT, 15 | OrientationLock.PORTRAIT_UP, 16 | ]; 17 | 18 | export const UseScreenOrientationLock: React.SFC = (props) => { 19 | const [orientation, setOrientation] = useState(OrientationLock.ALL); 20 | useScreenOrientationLock(orientation); 21 | 22 | return ( 23 | 27 | 28 | This example only uses the ScreenOrientation module. 29 | It renders multiple buttons to change the current screen orientation lock. 30 | 31 | 32 | {orientations.map(type => ( 33 | 34 | 41 | 42 | ))} 43 | 44 | 45 | ); 46 | }; 47 | 48 | UseScreenOrientationLock.defaultProps = { 49 | name: 'useScreenOrientationLock', 50 | description: 'locks the screen to an orientation with', 51 | }; 52 | 53 | const orientationName: Record = { 54 | [OrientationLock.DEFAULT]: 'default', 55 | [OrientationLock.ALL]: 'all', 56 | [OrientationLock.PORTRAIT]: 'portrait', 57 | [OrientationLock.PORTRAIT_UP]: 'portrait up', 58 | [OrientationLock.PORTRAIT_DOWN]: 'portrait down', 59 | [OrientationLock.LANDSCAPE]: 'landscape', 60 | [OrientationLock.LANDSCAPE_LEFT]: 'landscape left', 61 | [OrientationLock.LANDSCAPE_RIGHT]: 'landscape right', 62 | [OrientationLock.OTHER]: 'other', 63 | [OrientationLock.UNKNOWN]: 'unknown', 64 | }; 65 | -------------------------------------------------------------------------------- /example/src/molecules/screen-orientation/use-screen-orientation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useScreenOrientation } from '@use-expo/screen-orientation'; 4 | import { Orientation, SizeClassIOS } from 'expo-screen-orientation'; 5 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 6 | import { MoleculeProps } from '../../providers/molecule'; 7 | import { docs } from '../../providers/urls'; 8 | 9 | export const UseScreenOrientation: React.SFC = (props) => { 10 | const [orientation] = useScreenOrientation(); 11 | 12 | return ( 13 | 17 | 18 | This example only uses the ScreenOrientation module. 19 | It renders the current orientation and listens to changes. 20 | 21 | 22 | screen orientation 23 | {orientation?.orientation 24 | ? {orientationNames[orientation.orientation]} 25 | : unknown 26 | } 27 | {(!orientation?.verticalSizeClass || !orientation?.horizontalSizeClass) 28 | ? size class unavailable on this device 29 | : ( 30 | <> 31 | size class 32 | 33 | 34 | 35 | )} 36 | 37 | 38 | ); 39 | }; 40 | 41 | UseScreenOrientation.defaultProps = { 42 | name: 'useScreenOrientation', 43 | description: 'tracks changes in screen orientation', 44 | }; 45 | 46 | const orientationNames = { 47 | [Orientation.UNKNOWN]: 'unknown', 48 | [Orientation.PORTRAIT_UP]: 'portrait-up', 49 | [Orientation.PORTRAIT_DOWN]: 'portrait-down', 50 | [Orientation.LANDSCAPE_LEFT]: 'landscape-left', 51 | [Orientation.LANDSCAPE_RIGHT]: 'landscape-right', 52 | }; 53 | 54 | const sizeClassNames = { 55 | [SizeClassIOS.REGULAR]: 'regular', 56 | [SizeClassIOS.COMPACT]: 'compact', 57 | [SizeClassIOS.UNKNOWN]: 'unknown', 58 | }; 59 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-accelerometer'; 2 | export * from './use-barometer'; 3 | export * from './use-device-motion'; 4 | export * from './use-gyroscope'; 5 | export * from './use-magnetometer'; 6 | export * from './use-pedometer'; 7 | export * from './use-pedometer-history'; 8 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/use-accelerometer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { useAccelerometer } from '@use-expo/sensors'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseAccelerometer: React.SFC = (props) => { 9 | const [data, available] = useAccelerometer({ interval: 100 }); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the data from the Accelerometer module. 18 | It renders the three-dimensional data in a list, prefixed with the axis. 19 | 20 | 21 | {!available && ( 22 | Accelerometer is unavailable on this device. 23 | )} 24 | {(available && data) && ( 25 | <> 26 | 27 | 28 | 29 | 30 | )} 31 | 32 | 33 | ); 34 | }; 35 | 36 | UseAccelerometer.defaultProps = { 37 | name: 'useAccelerometer', 38 | description: 'tracks changes in acceleration', 39 | }; 40 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/use-barometer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { useBarometer } from '@use-expo/sensors'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseBarometer: React.SFC = (props) => { 9 | const [data, available] = useBarometer({ interval: 100 }); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the data from the Barometer module. 18 | It renders both the pressure and relative altitude in a list. 19 | 20 | 21 | {!available && ( 22 | Barometer is unavailable on this device. 23 | )} 24 | {(available && data) && ( 25 | <> 26 | 32 | 38 | 39 | )} 40 | 41 | 42 | ); 43 | }; 44 | 45 | UseBarometer.defaultProps = { 46 | name: 'useBarometer', 47 | description: 'tracks changes in air pressure', 48 | }; 49 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/use-gyroscope.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { useGyroscope } from '@use-expo/sensors'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseGyroscope: React.SFC = (props) => { 9 | const [data, available] = useGyroscope({ interval: 100 }); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the data from the Gyroscope module. 18 | It renders the three-dimensional data in a list, prefixed with the axis. 19 | 20 | 21 | {!available && ( 22 | Gyroscope is unavailable on this device. 23 | )} 24 | {(available && data) && ( 25 | <> 26 | 27 | 28 | 29 | 30 | )} 31 | 32 | 33 | ); 34 | }; 35 | 36 | UseGyroscope.defaultProps = { 37 | name: 'useGyroscope', 38 | description: 'tracks changes in rotation', 39 | }; 40 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/use-magnetometer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { useMagnetometer, useMagnetometerUncalibrated } from '@use-expo/sensors'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseMagnetometer: React.SFC = (props) => { 9 | const [calibrated, calibratedAvailable] = useMagnetometer({ interval: 200 }); 10 | const [uncalibrated, uncalibrartedAvailable] = useMagnetometerUncalibrated({ interval: 200 }); 11 | 12 | return ( 13 | 17 | 18 | This example fetches the data from the Magnetometer module. 19 | It renders the three-dimensional data in a list, prefixed with the axis. 20 | 21 | 22 | Calibrated 23 | {!calibratedAvailable && ( 24 | Magnetometer is unavailable on this device. 25 | )} 26 | {(calibratedAvailable && calibrated) && ( 27 | <> 28 | 29 | 30 | 31 | 32 | )} 33 | 34 | Uncalibrated 35 | {!uncalibrartedAvailable && ( 36 | Uncalibrated magnetometer is unavailable on this device. 37 | )} 38 | {(uncalibrartedAvailable && uncalibrated) && ( 39 | <> 40 | 41 | 42 | 43 | 44 | )} 45 | 46 | 47 | ); 48 | }; 49 | 50 | UseMagnetometer.defaultProps = { 51 | name: 'useMagnetometer', 52 | description: 'tracks changes in the magnetic field', 53 | }; 54 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/use-pedometer-history.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { usePedometerHistory } from '@use-expo/sensors'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UsePedometerHistory: React.SFC = (props) => { 9 | const [history, available] = usePedometerHistory({ 10 | start: new Date(Date.now() - 1000 * 60 * 60 * 24 * 7), 11 | end: new Date(), 12 | }); 13 | 14 | return ( 15 | 19 | 20 | This example fetches the "historical" steps count, within a date range, 21 | from the Pedometer module. 22 | 23 | 24 | Last two weeks 25 | {!available && ( 26 | Pedometer history is unavailable on this device 27 | )} 28 | {(available && history) && ( 29 | 30 | )} 31 | 32 | 33 | ); 34 | }; 35 | 36 | UsePedometerHistory.defaultProps = { 37 | name: 'usePedometerHistory', 38 | description: 'get historical step count', 39 | }; 40 | -------------------------------------------------------------------------------- /example/src/molecules/sensors/use-pedometer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption } from 'react-native-paper'; 3 | import { usePedometer } from '@use-expo/sensors'; 4 | import { Example, Information, Link, Measurement, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UsePedometer: React.SFC = (props) => { 9 | const [live, available] = usePedometer(); 10 | 11 | return ( 12 | 16 | 17 | This example fetches the "live" steps count from the Pedometer module. 18 | 19 | 20 | {!available && ( 21 | Pedometer is unavailable on this device 22 | )} 23 | {(available && live) && ( 24 | 25 | )} 26 | 27 | 28 | ); 29 | }; 30 | 31 | UsePedometer.defaultProps = { 32 | name: 'usePedometer', 33 | description: 'tracks live step count', 34 | }; 35 | -------------------------------------------------------------------------------- /example/src/molecules/store-review/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-store-review-has-action'; 2 | export * from './use-store-review-is-available'; 3 | export * from './use-store-review-request'; 4 | -------------------------------------------------------------------------------- /example/src/molecules/store-review/use-store-review-has-action.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useStoreReviewHasAction } from '@use-expo/store-review'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseStoreReviewHasAction: React.SFC = (props) => { 9 | const [hasAction] = useStoreReviewHasAction(); 10 | 11 | return ( 12 | 16 | 17 | This example determines if the store review can perform any action with the StoreReview module. 18 | 19 | 20 | Can perform an action for store review? 21 | {hasAction && {hasAction ? 'Yes' : 'No'}} 22 | 23 | 24 | ); 25 | }; 26 | 27 | UseStoreReviewHasAction.defaultProps = { 28 | name: 'useStoreReviewHasAction', 29 | description: 'determines if the store review can perform any action', 30 | }; 31 | -------------------------------------------------------------------------------- /example/src/molecules/store-review/use-store-review-is-available.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useStoreReviewIsAvailable } from '@use-expo/store-review'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseStoreReviewIsAvailable: React.SFC = (props) => { 9 | const [isAvailable] = useStoreReviewIsAvailable(); 10 | 11 | return ( 12 | 16 | 17 | This example determines if the platform has the capabilities to use request review with the StoreReview module. 18 | 19 | 20 | Can request a review? 21 | {isAvailable && {isAvailable ? 'Yes' : 'No'}} 22 | 23 | 24 | ); 25 | }; 26 | 27 | UseStoreReviewIsAvailable.defaultProps = { 28 | name: 'useStoreReviewIsAvailable', 29 | description: 'determines if the platform has the capabilities to use request review', 30 | }; 31 | -------------------------------------------------------------------------------- /example/src/molecules/store-review/use-store-review-request.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useStoreReviewRequest } from '@use-expo/store-review'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | 8 | export const UseStoreReviewRequest: React.SFC = (props) => { 9 | const requestReview = useStoreReviewRequest(); 10 | const [isRequested, setIsRequested] = useState(false); 11 | 12 | useEffect(() => { 13 | requestReview().then(setIsRequested); 14 | }, [requestReview]); 15 | 16 | return ( 17 | 21 | 22 | This example requests a store review if available with the StoreReview module. 23 | 24 | 25 | Is requested? 26 | {isRequested ? 'Yes' : 'No'} 27 | 28 | 29 | ); 30 | }; 31 | 32 | UseStoreReviewRequest.defaultProps = { 33 | name: 'useStoreReviewRequest', 34 | description: 'request a store review if available', 35 | }; 36 | -------------------------------------------------------------------------------- /example/src/molecules/web-browser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-browsers'; 2 | -------------------------------------------------------------------------------- /example/src/molecules/web-browser/use-browsers.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Caption, Text } from 'react-native-paper'; 3 | import { useBrowsers } from '@use-expo/web-browser'; 4 | import { Example, Information, Link, Page } from '../../atoms'; 5 | import { MoleculeProps } from '../../providers/molecule'; 6 | import { docs } from '../../providers/urls'; 7 | import { View } from 'react-native'; 8 | 9 | export const UseBrowsers: React.SFC = (props) => { 10 | const browsers = useBrowsers(); 11 | 12 | return ( 13 | 17 | 18 | This example fetches the available Android browsers from WebBrowser module. 19 | It renders all available browsers in a list. 20 | 21 | 22 | Available browsers: 23 | {browsers.map(name => ( 24 | 25 | {name} 26 | 27 | ))} 28 | 29 | 30 | ); 31 | }; 32 | 33 | UseBrowsers.defaultProps = { 34 | name: 'useBrowsers', 35 | description: 'Get a list of Android browsers', 36 | }; 37 | -------------------------------------------------------------------------------- /example/src/providers/assets.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { AppLoading } from 'expo'; 3 | import { useFonts } from '@use-expo/font'; 4 | 5 | export const AssetsProvider: React.SFC = (props) => { 6 | const [loaded] = useFonts({ 7 | 'source-sans-pro-medium': require('../assets/fonts/source-sans-pro/medium.ttf'), 8 | 'source-sans-pro-regular': require('../assets/fonts/source-sans-pro/regular.ttf'), 9 | 'source-sans-pro-light': require('../assets/fonts/source-sans-pro/light.ttf'), 10 | 'source-sans-pro-thin': require('../assets/fonts/source-sans-pro/thin.ttf'), 11 | }); 12 | 13 | return !loaded 14 | ? 15 | : props.children; 16 | }; 17 | 18 | interface AssetsProviderProps { 19 | children: React.ReactElement; 20 | } 21 | -------------------------------------------------------------------------------- /example/src/providers/molecule.tsx: -------------------------------------------------------------------------------- 1 | export interface MoleculeProps { 2 | /** The name of the hook or molecule */ 3 | name?: string; 4 | /** A short description about the hook or molecule */ 5 | description?: string; 6 | } 7 | -------------------------------------------------------------------------------- /example/src/providers/navigation.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from 'react-navigation'; 2 | import { createStackNavigator } from 'react-navigation-stack'; 3 | import { startCase } from 'lodash'; 4 | import { Overview, molecules } from '../molecules/overview'; 5 | 6 | const createScreens = () => { 7 | const stack: any = {}; 8 | 9 | Object.entries(molecules).forEach(([moleculeName, molecule]) => { 10 | const title = startCase(moleculeName); 11 | 12 | Object.values(molecule).forEach(screen => { 13 | stack[screen.defaultProps!.name!] = { 14 | screen, 15 | navigationOptions: { title }, 16 | }; 17 | }); 18 | }); 19 | 20 | return stack; 21 | }; 22 | 23 | const AppNavigator = createStackNavigator( 24 | { 25 | overview: { 26 | screen: Overview, 27 | navigationOptions: { header: null } 28 | }, 29 | ...createScreens(), 30 | }, 31 | { 32 | initialRouteName: 'overview', 33 | defaultNavigationOptions: { 34 | headerStyle: { 35 | backgroundColor: '#333', 36 | }, 37 | headerTintColor: '#fff', 38 | headerTitleStyle: { 39 | fontFamily: 'source-sans-pro-medium', 40 | }, 41 | }, 42 | }, 43 | ); 44 | 45 | export const NavigationProvider = createAppContainer(AppNavigator); 46 | -------------------------------------------------------------------------------- /example/src/providers/theme.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import merge from 'lodash/merge'; 3 | import { 4 | Theme, 5 | DefaultTheme, 6 | Provider as PaperProvider, 7 | } from 'react-native-paper'; 8 | 9 | export const ThemeProvider: React.SFC = (props) => ( 10 | 11 | {props.children} 12 | 13 | ); 14 | 15 | export const theme = merge(DefaultTheme, { 16 | fonts: { 17 | medium: 'source-sans-pro-medium', 18 | regular: 'source-sans-pro-regular', 19 | light: 'source-sans-pro-light', 20 | thin: 'source-sans-pro-thin', 21 | }, 22 | spaces: { 23 | small: 8, 24 | medium: 16, 25 | large: 32, 26 | huge: 64, 27 | }, 28 | } as UseExpoTheme); 29 | 30 | export type ThemeSpace = 'small' | 'medium' | 'large' | 'huge'; 31 | export interface UseExpoTheme extends Theme { 32 | spaces: { 33 | small: number; 34 | medium: number; 35 | large: number; 36 | huge: number; 37 | }; 38 | } 39 | 40 | export interface WithThemeProp { 41 | /** The use expo theme instance, injected by `withTheme` */ 42 | theme: UseExpoTheme; 43 | } 44 | -------------------------------------------------------------------------------- /example/src/providers/urls.tsx: -------------------------------------------------------------------------------- 1 | import { sample } from 'lodash'; 2 | 3 | export const docs = { 4 | application: 'https://docs.expo.io/versions/latest/sdk/application/', 5 | accelerometer: 'https://docs.expo.io/versions/latest/sdk/accelerometer/', 6 | barometer: 'https://docs.expo.io/versions/latest/sdk/barometer/', 7 | battery: 'https://docs.expo.io/versions/latest/sdk/battery/', 8 | brightness: 'https://docs.expo.io/versions/latest/sdk/brightness/', 9 | camera: 'https://docs.expo.io/versions/latest/sdk/camera/', 10 | deviceMotion: 'https://docs.expo.io/versions/latest/sdk/devicemotion/', 11 | font: 'https://docs.expo.io/versions/latest/sdk/font/', 12 | gyroscope: 'https://docs.expo.io/versions/latest/sdk/gyroscope/', 13 | magnetometer: 'https://docs.expo.io/versions/latest/sdk/magnetometer/', 14 | pedometer: 'https://docs.expo.io/versions/latest/sdk/pedometer/', 15 | permissions: 'https://docs.expo.io/versions/latest/sdk/permissions/', 16 | screenOrientation: 'https://docs.expo.io/versions/latest/sdk/screen-orientation/', 17 | storeReview: 'https://docs.expo.io/versions/latest/sdk/store-review/', 18 | webBrowser: 'https://docs.expo.io/versions/latest/sdk/webbrowser/', 19 | }; 20 | 21 | export const permissions = { 22 | camera: 'https://docs.expo.io/versions/latest/sdk/permissions/#permissionscamera', 23 | systemBrightness: 'https://docs.expo.io/versions/latest/sdk/permissions/#permissionssystem_brightness', 24 | }; 25 | 26 | export const expo = { 27 | permissions: 'https://github.com/expo/expo/tree/master/packages/expo-permissions#readme', 28 | }; 29 | 30 | export const repo = 'https://github.com/bycedric/use-expo'; 31 | 32 | // please don't judge me for this... 33 | export function author(): string { 34 | return sample([ 35 | 'https://finest.dev?ref=use-expo', 36 | 'https://cedric.dev?ref=use-expo', 37 | 'https://bycedric.dev?ref=use-expo', 38 | 'https://dekoningdernederlanden.nl?ref=use-expo', 39 | 'https://bycedric.com?ref=use-expo', 40 | ])!; 41 | } 42 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-expo', 3 | clearMocks: true, 4 | testMatch: [ 5 | '/packages/**/*.test.ts', 6 | ], 7 | moduleNameMapper: { 8 | '@use-expo/([^/]+)': '/packages/$1/src' 9 | }, 10 | modulePathIgnorePatterns: [ 11 | '/example', 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent", 3 | "packages": [ 4 | "example", 5 | "packages/*" 6 | ], 7 | "npmClient": "yarn", 8 | "useWorkspaces": true, 9 | "command": { 10 | "publish": { 11 | "allowBranch": "master", 12 | "conventionalCommits": true, 13 | "createRelease": "github", 14 | "ignoreChanges": [ 15 | "**/__fixtures__/**", 16 | "**/__tests__/**", 17 | "**/*.md", 18 | "**/example/**", 19 | "*.spec.ts*", 20 | "*.test.ts*" 21 | ], 22 | "message": "chore: publish new version", 23 | "preDistTag": "next", 24 | "preId": "alpha", 25 | "tagVersionPrefix": "" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@use-expo/monorepo", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "author": "Cedric van Putten (https://bycedric.com)", 7 | "homepage": "https://github.com/bycedric/use-expo/tree/master#readme", 8 | "bugs": { 9 | "url": "https://github.com/bycedric/use-expo/issues" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/bycedric/use-expo.git" 14 | }, 15 | "scripts": { 16 | "lint": "tsc --noEmit --composite false && eslint . --ext ts,tsx,js,jsx", 17 | "test": "jest", 18 | "build": "lerna run build", 19 | "publish": "lerna publish", 20 | "example": "yarn --cwd example" 21 | }, 22 | "devDependencies": { 23 | "@commitlint/cli": "^8.3.5", 24 | "@commitlint/config-conventional": "^8.3.4", 25 | "@testing-library/react-hooks": "^3.2.1", 26 | "@types/jest": "^25.2.1", 27 | "@types/react": "^16.9.32", 28 | "@typescript-eslint/eslint-plugin": "^2.26.0", 29 | "@typescript-eslint/parser": "^2.26.0", 30 | "eslint": "^6.8.0", 31 | "eslint-plugin-jest": "^23.8.2", 32 | "eslint-plugin-react": "^7.19.0", 33 | "eslint-plugin-react-hooks": "^4.0.0", 34 | "jest": "^25.2.7", 35 | "jest-expo": "^37.0.0", 36 | "lerna": "^3.20.2", 37 | "react": "16.9.0", 38 | "typescript": "^3.8.3" 39 | }, 40 | "publishConfig": { 41 | "registry": "https://registry.npmjs.org/" 42 | }, 43 | "resolutions": { 44 | "expo": "37.0.0", 45 | "react": "16.9.0", 46 | "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.0.tar.gz", 47 | "react-test-renderer": "16.9.0", 48 | "typescript": "^3.8.3" 49 | }, 50 | "workspaces": { 51 | "packages": [ 52 | "example", 53 | "packages/*" 54 | ] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/application/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 | # 1.0.0 (2020-05-03) 7 | 8 | 9 | ### Features 10 | 11 | * **application:** create new expo application hooks ([#238](https://github.com/bycedric/use-expo/issues/238)) ([d307a50](https://github.com/bycedric/use-expo/commit/d307a50b8b05dceef99f8327a32bd3afa5b79091)) 12 | -------------------------------------------------------------------------------- /packages/application/README.md: -------------------------------------------------------------------------------- 1 |
2 |

application hooks

3 |

Complementary hooks for Expo Application

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/application expo-application
25 |
26 |
27 | 28 | - [`useApplicationInstallTime`](./docs/use-application-install-time.md)  —  get the time the app was installed on the device 29 | - [`useApplicationAndroidInstallReferrer`](./docs/use-application-android-install-referrer.md)  —  get the referrer URL of the installed app 30 | - [`useApplicationAndroidLastUpdateTime`](./docs/use-application-android-last-update-time.md)  —  get the time the app was last updated via the Google Play Store 31 | - [`useApplicationIosIdForVendor`](./docs/use-application-ios-id-for-vendor.md)  —  get the referrer URL of the installed app 32 | 33 |
34 |
35 | with :heart: byCedric 36 |
37 |
38 | -------------------------------------------------------------------------------- /packages/application/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/application", 3 | "version": "1.0.0", 4 | "description": "Complementary hooks for expo-application", 5 | "keywords": [ 6 | "expo", 7 | "application", 8 | "hooks" 9 | ], 10 | "license": "MIT", 11 | "author": "Cedric van Putten (https://bycedric.com)", 12 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/application#readme", 13 | "bugs": { 14 | "url": "https://github.com/bycedric/use-expo/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/use-expo.git", 19 | "directory": "packages/application" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "tests" 24 | }, 25 | "main": "build/commonjs/index.js", 26 | "react-native": "src/index.ts", 27 | "module": "build/module/index.js", 28 | "types": "build/typescript/src/index.d.ts", 29 | "files": [ 30 | "build", 31 | "src" 32 | ], 33 | "scripts": { 34 | "prepare": "bob build", 35 | "build": "bob build" 36 | }, 37 | "devDependencies": { 38 | "@react-native-community/bob": "^0.14.3", 39 | "@testing-library/react-hooks": "^3.2.1", 40 | "@types/react": "~16.9.32", 41 | "expo-application": "~2.1.1", 42 | "react": "16.9.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "peerDependencies": { 46 | "expo-application": ">=2.1.0" 47 | }, 48 | "@react-native-community/bob": { 49 | "source": "src", 50 | "output": "build", 51 | "targets": [ 52 | "commonjs", 53 | "module", 54 | "typescript" 55 | ] 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/application/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-application-android-install-referrer'; 2 | export * from './use-application-android-last-update-time'; 3 | export * from './use-application-install-time'; 4 | export * from './use-application-ios-id-for-vendor'; 5 | -------------------------------------------------------------------------------- /packages/application/src/use-application-android-install-referrer.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getInstallReferrerAsync } from 'expo-application'; 3 | 4 | /** 5 | * Get the referrer URL of the installed app from the Google Play Store. 6 | * This URL may not be a complete or absolute URL. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/application/#applicationgetinstallreferrerasync 9 | * @see https://developer.android.com/google/play/installreferrer 10 | * @example const [installReferrer, getInstallReferrer] = useApplicationAndroidInstallReferrer(...); 11 | */ 12 | export function useApplicationAndroidInstallReferrer( 13 | options: ApplicationAndroidInstallReferrerOptions = {}, 14 | ): [ 15 | string | undefined, 16 | () => Promise, 17 | ] { 18 | const [applicationInstallReferrer, setApplicationInstallReferrer] = useState(); 19 | const { get = true } = options; 20 | 21 | const getApplicationInstallReferrer = useCallback(() => ( 22 | getInstallReferrerAsync().then(setApplicationInstallReferrer) 23 | ), []); 24 | 25 | useEffect(() => { 26 | if (get) { 27 | getApplicationInstallReferrer(); 28 | } 29 | }, [get, getApplicationInstallReferrer]); 30 | 31 | return [applicationInstallReferrer, getApplicationInstallReferrer]; 32 | } 33 | 34 | export interface ApplicationAndroidInstallReferrerOptions { 35 | /** If it should fetch the application install referrer when mounted, defaults to `true` */ 36 | get?: boolean; 37 | } 38 | -------------------------------------------------------------------------------- /packages/application/src/use-application-android-last-update-time.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getLastUpdateTimeAsync } from 'expo-application'; 3 | 4 | /** 5 | * Get the time the app was last updated via the Google Play Store. 6 | * 7 | * @see https://docs.expo.io/versions/latest/sdk/application/#applicationgetlastupdatetimeasync 8 | * @example const [lastUpdateTime, getLastUpdateTime] = useApplicationAndroidLastUpdateTime(...); 9 | */ 10 | export function useApplicationAndroidLastUpdateTime( 11 | options: ApplicationAndroidLastUpdateTimeOptions = {}, 12 | ): [ 13 | Date | undefined, 14 | () => Promise, 15 | ] { 16 | const [applicationLastUpdateTime, setApplicationLastUpdateTime] = useState(); 17 | const { get = true } = options; 18 | 19 | const getApplicationLastUpdateTime = useCallback(() => ( 20 | getLastUpdateTimeAsync().then(setApplicationLastUpdateTime) 21 | ), []); 22 | 23 | useEffect(() => { 24 | if (get) { 25 | getApplicationLastUpdateTime(); 26 | } 27 | }, [get, getApplicationLastUpdateTime]); 28 | 29 | return [applicationLastUpdateTime, getApplicationLastUpdateTime]; 30 | } 31 | 32 | export interface ApplicationAndroidLastUpdateTimeOptions { 33 | /** If it should fetch the application last update time when mounted, defaults to `true` */ 34 | get?: boolean; 35 | } 36 | -------------------------------------------------------------------------------- /packages/application/src/use-application-install-time.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getInstallationTimeAsync } from 'expo-application'; 3 | 4 | /** 5 | * Get the time the app was installed on the device, not counting subsequent updates. 6 | * If the app is uninstalled and reinstalled, it returns the time when reinstalled. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/application/#applicationgetinstallationtimeasync 9 | * @example const [installTime, getInstallTime] = useApplicationInstallTime(...); 10 | */ 11 | export function useApplicationInstallTime( 12 | options: ApplicationInstallTimeOptions = {}, 13 | ): [ 14 | Date | undefined, 15 | () => Promise, 16 | ] { 17 | const [applicationInstallTime, setApplicationInstallTime] = useState(); 18 | const { get = true } = options; 19 | 20 | const getApplicationInstallTime = useCallback(() => ( 21 | getInstallationTimeAsync().then(setApplicationInstallTime) 22 | ), []); 23 | 24 | useEffect(() => { 25 | if (get) { 26 | getApplicationInstallTime(); 27 | } 28 | }, [get, getApplicationInstallTime]); 29 | 30 | return [applicationInstallTime, getApplicationInstallTime]; 31 | } 32 | 33 | export interface ApplicationInstallTimeOptions { 34 | /** If it should fetch the application install time when mounted, defaults to `true` */ 35 | get?: boolean; 36 | } 37 | -------------------------------------------------------------------------------- /packages/application/src/use-application-ios-id-for-vendor.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getIosIdForVendorAsync } from 'expo-application'; 3 | 4 | /** 5 | * Get the iOS "identifier for Vendor" (IDFV), an unique ID for device identificiation by app vendor. 6 | * This ID is changed when the user removed all apps from the vendor. 7 | * 8 | * Note, when the device is restarted before unlocking the device this may return no ID. 9 | * 10 | * @see https://docs.expo.io/versions/latest/sdk/application/#applicationgetiosidforvendorasync 11 | * @see https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor 12 | * @example const [vendorId, getVendorId] = useApplicationIosIdForVendor(...); 13 | */ 14 | export function useApplicationIosIdForVendor( 15 | options: ApplicationIosIdForVendorOptions = {}, 16 | ): [ 17 | string | undefined, 18 | () => Promise, 19 | ] { 20 | const [applicationIdForVendor, setApplicationIdForVendor] = useState(); 21 | const { get = true } = options; 22 | 23 | const getApplicationIdForVendor = useCallback(() => ( 24 | getIosIdForVendorAsync().then(setApplicationIdForVendor) 25 | ), []); 26 | 27 | useEffect(() => { 28 | if (get) { 29 | getApplicationIdForVendor(); 30 | } 31 | }, [get, getApplicationIdForVendor]); 32 | 33 | return [applicationIdForVendor, getApplicationIdForVendor]; 34 | } 35 | 36 | export interface ApplicationIosIdForVendorOptions { 37 | /** If it should fetch the application vendor id when mounted, defaults to `true` */ 38 | get?: boolean; 39 | } 40 | -------------------------------------------------------------------------------- /packages/application/tests/use-application-android-install-referrer.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as Application from 'expo-application'; 3 | import { useApplicationAndroidInstallReferrer } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | const referrer = 'utm_source=google-play&utm_medium=organic'; 9 | 10 | it('returns data and get callback when mounted', () => { 11 | const hook = renderHook(() => useApplicationAndroidInstallReferrer({ get: false })); 12 | 13 | expect(hook.result.current[DATA]).toBeUndefined(); 14 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 15 | }); 16 | 17 | describe('get callback', () => { 18 | it('updates data with get callback', async () => { 19 | jest.spyOn(Application, 'getInstallReferrerAsync') 20 | .mockResolvedValue(referrer); 21 | 22 | const hook = renderHook(() => useApplicationAndroidInstallReferrer({ get: false })); 23 | expect(hook.result.current[DATA]).toBeUndefined(); 24 | await act(() => hook.result.current[GET]()); 25 | 26 | expect(hook.result.current[DATA]).toEqual(referrer); 27 | }); 28 | 29 | it('uses the same get callback when rerendered', async () => { 30 | const hook = renderHook(() => useApplicationAndroidInstallReferrer({ get: false })); 31 | const getter = hook.result.current[GET]; 32 | hook.rerender({ get: false }); 33 | 34 | expect(getter).toBe(hook.result.current[GET]); 35 | }); 36 | }); 37 | 38 | describe('get option', () => { 39 | it('gets application install referrer when mounted', async () => { 40 | jest.spyOn(Application, 'getInstallReferrerAsync') 41 | .mockResolvedValue(referrer); 42 | 43 | const hook = renderHook(() => useApplicationAndroidInstallReferrer({ get: true })); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(referrer); 47 | }); 48 | 49 | it('updates data with get option when rerendered', async () => { 50 | jest.spyOn(Application, 'getInstallReferrerAsync') 51 | .mockResolvedValue(referrer); 52 | 53 | const hook = renderHook(useApplicationAndroidInstallReferrer, { initialProps: { get: false } }); 54 | expect(hook.result.current[DATA]).toBeUndefined(); 55 | hook.rerender({ get: true }) 56 | await hook.waitForNextUpdate(); 57 | 58 | expect(hook.result.current[DATA]).toBe(referrer); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /packages/application/tests/use-application-android-last-update-time.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as Application from 'expo-application'; 3 | import { useApplicationAndroidLastUpdateTime } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | const lastUpdateTime = new Date('2020-04-26 00:00:00'); 9 | 10 | it('returns data and get callback when mounted', () => { 11 | const hook = renderHook(() => useApplicationAndroidLastUpdateTime({ get: false })); 12 | 13 | expect(hook.result.current[DATA]).toBeUndefined(); 14 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 15 | }); 16 | 17 | describe('get callback', () => { 18 | it('updates data with get callback', async () => { 19 | jest.spyOn(Application, 'getLastUpdateTimeAsync') 20 | .mockResolvedValue(lastUpdateTime); 21 | 22 | const hook = renderHook(() => useApplicationAndroidLastUpdateTime({ get: false })); 23 | expect(hook.result.current[DATA]).toBeUndefined(); 24 | await act(() => hook.result.current[GET]()); 25 | 26 | expect(hook.result.current[DATA]).toMatchObject(lastUpdateTime); 27 | }); 28 | 29 | it('uses the same get callback when rerendered', async () => { 30 | const hook = renderHook(() => useApplicationAndroidLastUpdateTime({ get: false })); 31 | const getter = hook.result.current[GET]; 32 | hook.rerender({ get: false }); 33 | 34 | expect(getter).toBe(hook.result.current[GET]); 35 | }); 36 | }); 37 | 38 | describe('get option', () => { 39 | it('gets application last update time when mounted', async () => { 40 | jest.spyOn(Application, 'getLastUpdateTimeAsync') 41 | .mockResolvedValue(lastUpdateTime); 42 | 43 | const hook = renderHook(() => useApplicationAndroidLastUpdateTime({ get: true })); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(lastUpdateTime); 47 | }); 48 | 49 | it('updates data with get option when rerendered', async () => { 50 | jest.spyOn(Application, 'getLastUpdateTimeAsync') 51 | .mockResolvedValue(lastUpdateTime); 52 | 53 | const hook = renderHook(useApplicationAndroidLastUpdateTime, { initialProps: { get: false } }); 54 | expect(hook.result.current[DATA]).toBeUndefined(); 55 | hook.rerender({ get: true }) 56 | await hook.waitForNextUpdate(); 57 | 58 | expect(hook.result.current[DATA]).toBe(lastUpdateTime); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /packages/application/tests/use-application-install-time.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as Application from 'expo-application'; 3 | import { useApplicationInstallTime } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | const installTime = new Date('2020-04-26 00:00:00'); 9 | 10 | it('returns data and get callback when mounted', () => { 11 | const hook = renderHook(() => useApplicationInstallTime({ get: false })); 12 | 13 | expect(hook.result.current[DATA]).toBeUndefined(); 14 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 15 | }); 16 | 17 | describe('get callback', () => { 18 | it('updates data with get callback', async () => { 19 | jest.spyOn(Application, 'getInstallationTimeAsync') 20 | .mockResolvedValue(installTime); 21 | 22 | const hook = renderHook(() => useApplicationInstallTime({ get: false })); 23 | expect(hook.result.current[DATA]).toBeUndefined(); 24 | await act(() => hook.result.current[GET]()); 25 | 26 | expect(hook.result.current[DATA]).toMatchObject(installTime); 27 | }); 28 | 29 | it('uses the same get callback when rerendered', async () => { 30 | const hook = renderHook(() => useApplicationInstallTime({ get: false })); 31 | const getter = hook.result.current[GET]; 32 | hook.rerender({ get: false }); 33 | 34 | expect(getter).toBe(hook.result.current[GET]); 35 | }); 36 | }); 37 | 38 | describe('get option', () => { 39 | it('gets application install time when mounted', async () => { 40 | jest.spyOn(Application, 'getInstallationTimeAsync') 41 | .mockResolvedValue(installTime); 42 | 43 | const hook = renderHook(() => useApplicationInstallTime({ get: true })); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(installTime); 47 | }); 48 | 49 | it('updates data with get option when rerendered', async () => { 50 | jest.spyOn(Application, 'getInstallationTimeAsync') 51 | .mockResolvedValue(installTime); 52 | 53 | const hook = renderHook(useApplicationInstallTime, { initialProps: { get: false } }); 54 | expect(hook.result.current[DATA]).toBeUndefined(); 55 | hook.rerender({ get: true }) 56 | await hook.waitForNextUpdate(); 57 | 58 | expect(hook.result.current[DATA]).toBe(installTime); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /packages/application/tests/use-application-ios-id-for-vendor.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as Application from 'expo-application'; 3 | import { useApplicationIosIdForVendor } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | const vendorId = '68753A44-4D6F-1226-9C60-0050E4C00067'; 9 | 10 | it('returns data and get callback when mounted', () => { 11 | const hook = renderHook(() => useApplicationIosIdForVendor({ get: false })); 12 | 13 | expect(hook.result.current[DATA]).toBeUndefined(); 14 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 15 | }); 16 | 17 | describe('get callback', () => { 18 | it('updates data with get callback', async () => { 19 | jest.spyOn(Application, 'getIosIdForVendorAsync') 20 | .mockResolvedValue(vendorId); 21 | 22 | const hook = renderHook(() => useApplicationIosIdForVendor({ get: false })); 23 | expect(hook.result.current[DATA]).toBeUndefined(); 24 | await act(() => hook.result.current[GET]()); 25 | 26 | expect(hook.result.current[DATA]).toEqual(vendorId); 27 | }); 28 | 29 | it('uses the same get callback when rerendered', async () => { 30 | const hook = renderHook(() => useApplicationIosIdForVendor({ get: false })); 31 | const getter = hook.result.current[GET]; 32 | hook.rerender({ get: false }); 33 | 34 | expect(getter).toBe(hook.result.current[GET]); 35 | }); 36 | }); 37 | 38 | describe('get option', () => { 39 | it('gets application vendor id when mounted', async () => { 40 | jest.spyOn(Application, 'getIosIdForVendorAsync') 41 | .mockResolvedValue(vendorId); 42 | 43 | const hook = renderHook(() => useApplicationIosIdForVendor({ get: true })); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(vendorId); 47 | }); 48 | 49 | it('updates data with get option when rerendered', async () => { 50 | jest.spyOn(Application, 'getIosIdForVendorAsync') 51 | .mockResolvedValue(vendorId); 52 | 53 | const hook = renderHook(useApplicationIosIdForVendor, { initialProps: { get: false } }); 54 | expect(hook.result.current[DATA]).toBeUndefined(); 55 | hook.rerender({ get: true }) 56 | await hook.waitForNextUpdate(); 57 | 58 | expect(hook.result.current[DATA]).toBe(vendorId); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /packages/application/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/battery/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Code Refactoring 10 | 11 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 17 | 18 | * refactor(battery): upgrade to expo sdk 37 19 | 20 | * refactor(brightness): upgrade to expo sdk 37 21 | 22 | * refactor(font): upgrade to expo sdk 37 23 | 24 | * refactor(permissions): upgrade to expo sdk 37 25 | 26 | * refactor(sensors): upgrade to expo sdk 37 27 | 28 | * refactor: upgrade monorepo to expo sdk 37 29 | 30 | * fix: use compatible semver range for react types 31 | 32 | 33 | 34 | 35 | 36 | ## [1.0.1](https://github.com/bycedric/use-expo/compare/1.0.0...1.0.1) (2020-02-25) 37 | 38 | **Note:** Version bump only for package @use-expo/battery 39 | 40 | 41 | 42 | 43 | 44 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **battery:** unify tests and fix callback reuse ([#64](https://github.com/bycedric/use-expo/issues/64)) ([0721bc4](https://github.com/bycedric/use-expo/commit/0721bc42f625aad38c504303e57bae13bc919eb7)) 50 | -------------------------------------------------------------------------------- /packages/battery/README.md: -------------------------------------------------------------------------------- 1 |
2 |

battery hooks

3 |

Complementary hooks for Expo Battery

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/battery expo-battery
25 |
26 |
27 | 28 | - [`useBattery`](./docs/use-battery.md)  —  get the battery level, state and power mode 29 | - [`useBatteryLevel`](./docs/use-battery-level.md)  —  get or track the battery level or percentage remaining 30 | - [`useBatteryLowPowerMode`](./docs/use-battery-low-power-mode.md)  —  get or track the battery low power mode 31 | - [`useBatteryState`](./docs/use-battery-state.md)  —  get or track the battery (charging) state 32 | 33 |
34 |
35 | with :heart: byCedric 36 |
37 |
38 | -------------------------------------------------------------------------------- /packages/battery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/battery", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo-battery", 5 | "keywords": [ 6 | "expo", 7 | "battery", 8 | "hooks" 9 | ], 10 | "license": "MIT", 11 | "author": "Cedric van Putten (https://bycedric.com)", 12 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/battery#readme", 13 | "bugs": { 14 | "url": "https://github.com/bycedric/use-expo/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/use-expo.git", 19 | "directory": "packages/battery" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "tests" 24 | }, 25 | "main": "build/commonjs/index.js", 26 | "react-native": "src/index.ts", 27 | "module": "build/module/index.js", 28 | "types": "build/typescript/src/index.d.ts", 29 | "files": [ 30 | "build", 31 | "src" 32 | ], 33 | "scripts": { 34 | "prepare": "bob build", 35 | "build": "bob build" 36 | }, 37 | "devDependencies": { 38 | "@react-native-community/bob": "^0.14.3", 39 | "@testing-library/react-hooks": "^3.2.1", 40 | "@types/react": "~16.9.32", 41 | "expo-battery": "~2.2.1", 42 | "react": "16.9.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "peerDependencies": { 46 | "expo-battery": ">=2.1.0" 47 | }, 48 | "@react-native-community/bob": { 49 | "source": "src", 50 | "output": "build", 51 | "targets": [ 52 | "commonjs", 53 | "module", 54 | "typescript" 55 | ] 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/battery/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-battery'; 2 | export * from './use-battery-level'; 3 | export * from './use-battery-low-power-mode'; 4 | export * from './use-battery-state'; 5 | -------------------------------------------------------------------------------- /packages/battery/src/use-battery-level.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getBatteryLevelAsync, addBatteryLevelListener } from 'expo-battery'; 3 | 4 | /** 5 | * Get or track the battery level of the device. 6 | * It returns a number between `0..1`, which represents the percentage of power left. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/battery/#batterygetbatterylevelasync 9 | * @remarks For older devices without this feature, it always returns `-1`. 10 | * @example const [batteryLevel, getBatteryLevel] = useBatteryLevel(...); 11 | */ 12 | export function useBatteryLevel( 13 | options: BatteryLevelOptions = {}, 14 | ): [ 15 | number | undefined, 16 | () => Promise, 17 | ] { 18 | const [data, setData] = useState(); 19 | const { 20 | get = true, 21 | listen = true, 22 | } = options; 23 | 24 | const getBatteryLevel = useCallback(() => ( 25 | getBatteryLevelAsync().then(setData) 26 | ), []); 27 | 28 | useEffect(() => { 29 | if (get) { 30 | getBatteryLevel(); 31 | } 32 | 33 | if (listen) { 34 | return addBatteryLevelListener(state => setData(state.batteryLevel)).remove; 35 | } 36 | }, [get, getBatteryLevel, listen]); 37 | 38 | return [data, getBatteryLevel]; 39 | } 40 | 41 | export interface BatteryLevelOptions { 42 | /** If it should fetch the battery level when mounted, defaults to `true` */ 43 | get?: boolean; 44 | /** If it should listen to any change in battery level, defaults to `true` */ 45 | listen?: boolean; 46 | } 47 | -------------------------------------------------------------------------------- /packages/battery/src/use-battery-low-power-mode.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { isLowPowerModeEnabledAsync, addLowPowerModeListener } from 'expo-battery'; 3 | 4 | /** 5 | * Get or track the low power mode of the device. 6 | * It returns `true` when enabled, or `false` when disabled. 7 | * This feature is also known as battery save mode. 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/battery/#batteryislowpowermodeenabledasync 10 | * @remarks For older devices without this feature, it always returns `false`. 11 | * @example const [isLowPowerMode, getLowPowerMode] = useBatteryLowPowerMode(...); 12 | */ 13 | export function useBatteryLowPowerMode( 14 | options: BatteryLowPowerModeOptions = {}, 15 | ): [ 16 | boolean | undefined, 17 | () => Promise, 18 | ] { 19 | const [data, setData] = useState(); 20 | const { 21 | get = true, 22 | listen = true, 23 | } = options; 24 | 25 | const getBatteryLowPowerMode = useCallback(() => ( 26 | isLowPowerModeEnabledAsync().then(setData) 27 | ), []); 28 | 29 | useEffect(() => { 30 | if (get) { 31 | getBatteryLowPowerMode(); 32 | } 33 | 34 | if (listen) { 35 | return addLowPowerModeListener(state => setData(state.lowPowerMode)).remove; 36 | } 37 | }, [get, getBatteryLowPowerMode, listen]); 38 | 39 | return [data, getBatteryLowPowerMode]; 40 | } 41 | 42 | export interface BatteryLowPowerModeOptions { 43 | /** If it should fetch the battery low power mode status when mounted, defaults to `true` */ 44 | get?: boolean; 45 | /** If it should listen to any change in battery low power mode status, defaults to `true` */ 46 | listen?: boolean; 47 | } 48 | -------------------------------------------------------------------------------- /packages/battery/src/use-battery-state.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { BatteryState, getBatteryStateAsync, addBatteryStateListener } from 'expo-battery'; 3 | 4 | /** 5 | * Get or track the battery state of the device. 6 | * It returns the `BatteryState` enum with one of the following values: 7 | * - 0 `UNKNOWN` if the battery state is unknown or unable to access 8 | * - 1 `UNPLUGGED` if the battery is not charging or discharging 9 | * - 2 `CHARGING` if the battery is charging 10 | * - 3 `FULL` if the battery level is full 11 | * 12 | * @see https://docs.expo.io/versions/latest/sdk/battery/#batterygetbatterystateasync 13 | * @example const [batteryState, getBatteryState] = useBatteryState(...); 14 | */ 15 | export function useBatteryState( 16 | options: BatteryStateOptions = {} 17 | ): [ 18 | BatteryState | undefined, 19 | () => Promise, 20 | ] { 21 | const [data, setData] = useState(); 22 | const { 23 | get = true, 24 | listen = true, 25 | } = options; 26 | 27 | const getBatteryState = useCallback(() => ( 28 | getBatteryStateAsync().then(setData) 29 | ), []); 30 | 31 | useEffect(() => { 32 | if (get) { 33 | getBatteryState(); 34 | } 35 | 36 | if (listen) { 37 | return addBatteryStateListener(state => setData(state.batteryState)).remove; 38 | } 39 | }, [get, getBatteryState, listen]); 40 | 41 | return [data, getBatteryState]; 42 | } 43 | 44 | export interface BatteryStateOptions { 45 | /** If it should fetch the battery state when mounted, defaults to `true` */ 46 | get?: boolean; 47 | /** If it should listen to any change in battery state, defaults to `true` */ 48 | listen?: boolean; 49 | } 50 | -------------------------------------------------------------------------------- /packages/battery/src/use-battery.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useCallback } from 'react'; 2 | import { PowerState, getPowerStateAsync } from 'expo-battery'; 3 | 4 | /** 5 | * Get the battery power state from the device. 6 | * It returns a "snapshot" of the following data: 7 | * - battery level (percentage of power left) 8 | * - current battery state (e.g., charging or unplugged) 9 | * - if low power mode is turned on (a.k.a battery saver) 10 | * 11 | * @see https://docs.expo.io/versions/latest/sdk/battery/#batterygetpowerstateasync 12 | * @remarks This does not track "live" updates of these values. 13 | * @example const [powerState, getPowerState] = useBattery(...); 14 | */ 15 | export function useBattery( 16 | options: BatteryOptions = {}, 17 | ): [ 18 | PowerState | undefined, 19 | () => Promise, 20 | ] { 21 | const [data, setData] = useState(); 22 | const { get = true } = options; 23 | 24 | const getBatteryPowerState = useCallback(() => ( 25 | getPowerStateAsync().then(setData) 26 | ), []); 27 | 28 | useEffect(() => { 29 | if (get) { 30 | getBatteryPowerState(); 31 | } 32 | }, [get, getBatteryPowerState]); 33 | 34 | return [data, getBatteryPowerState]; 35 | } 36 | 37 | export interface BatteryOptions { 38 | /** If it should fetch the battery power state when mounted, defaults to `true` */ 39 | get?: boolean; 40 | } 41 | -------------------------------------------------------------------------------- /packages/battery/tests/use-battery.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as Battery from 'expo-battery'; 3 | import { useBattery } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | const powerState: Battery.PowerState = { 9 | batteryLevel: 0.1337, 10 | batteryState: Battery.BatteryState.CHARGING, 11 | lowPowerMode: true, 12 | }; 13 | 14 | it('returns data and get callback when mounted', () => { 15 | const hook = renderHook(() => useBattery({ get: false })); 16 | 17 | expect(hook.result.current[DATA]).toBeUndefined(); 18 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 19 | }); 20 | 21 | describe('get callback', () => { 22 | it('updates data with get callback', async () => { 23 | jest.spyOn(Battery, 'getPowerStateAsync') 24 | .mockResolvedValue(powerState); 25 | 26 | const hook = renderHook(() => useBattery({ get: false })); 27 | expect(hook.result.current[DATA]).toBeUndefined(); 28 | await act(() => hook.result.current[GET]()); 29 | 30 | expect(hook.result.current[DATA]).toMatchObject(powerState); 31 | }); 32 | 33 | it('uses the same get callback when rerendered', async () => { 34 | const hook = renderHook(() => useBattery({ get: false })); 35 | const getter = hook.result.current[GET]; 36 | hook.rerender({ get: false }); 37 | 38 | expect(getter).toBe(hook.result.current[GET]); 39 | }); 40 | }); 41 | 42 | describe('get option', () => { 43 | it('gets battery power state when mounted', async () => { 44 | jest.spyOn(Battery, 'getPowerStateAsync') 45 | .mockResolvedValue(powerState); 46 | 47 | const hook = renderHook(() => useBattery({ get: true })); 48 | await hook.waitForNextUpdate(); 49 | 50 | expect(hook.result.current[DATA]).toBe(powerState); 51 | }); 52 | 53 | it('updates data with get option when rerendered', async () => { 54 | jest.spyOn(Battery, 'getPowerStateAsync') 55 | .mockResolvedValue(powerState); 56 | 57 | const hook = renderHook(useBattery, { initialProps: { get: false } }); 58 | expect(hook.result.current[DATA]).toBeUndefined(); 59 | hook.rerender({ get: true }) 60 | await hook.waitForNextUpdate(); 61 | 62 | expect(hook.result.current[DATA]).toBe(powerState); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /packages/battery/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/brightness/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Code Refactoring 10 | 11 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 17 | 18 | * refactor(battery): upgrade to expo sdk 37 19 | 20 | * refactor(brightness): upgrade to expo sdk 37 21 | 22 | * refactor(font): upgrade to expo sdk 37 23 | 24 | * refactor(permissions): upgrade to expo sdk 37 25 | 26 | * refactor(sensors): upgrade to expo sdk 37 27 | 28 | * refactor: upgrade monorepo to expo sdk 37 29 | 30 | * fix: use compatible semver range for react types 31 | 32 | 33 | 34 | 35 | 36 | ## [1.0.1](https://github.com/bycedric/use-expo/compare/1.0.0...1.0.1) (2020-02-25) 37 | 38 | **Note:** Version bump only for package @use-expo/brightness 39 | 40 | 41 | 42 | 43 | 44 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **brightness:** fetch brighness after new props ([af1ce94](https://github.com/bycedric/use-expo/commit/af1ce94d23e74e736be7f1dcfc4f3a46d1ecc59d)) 50 | * **brightness:** fetch brighness mode after new props ([#49](https://github.com/bycedric/use-expo/issues/49)) ([56f2ff5](https://github.com/bycedric/use-expo/commit/56f2ff58c94c45866ed94fa976a52a5c0223e6ef)) 51 | -------------------------------------------------------------------------------- /packages/brightness/README.md: -------------------------------------------------------------------------------- 1 |
2 |

brightness hooks

3 |

Complementary hooks for Expo Brightness

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/brightness expo-brightness
25 |
26 |
27 | 28 | - [`useBrightness`](./docs/use-brightness.md)  —  change or track the screen brightness 29 | - [`useSystemBrightness`](./docs/use-system-brightness.md)  —  change or track the system screen brightness 30 | - [`useSystemBrightnessMode`](./docs/use-system-brightness-mode.md)  —  change or track the system brightness mode 31 | 32 |
33 |
34 | with :heart: byCedric 35 |
36 |
37 | -------------------------------------------------------------------------------- /packages/brightness/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/brightness", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo-brightness", 5 | "keywords": [ 6 | "expo", 7 | "brightness", 8 | "hooks" 9 | ], 10 | "license": "MIT", 11 | "author": "Cedric van Putten (https://bycedric.com)", 12 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/brightness#readme", 13 | "bugs": { 14 | "url": "https://github.com/bycedric/use-expo/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/use-expo.git", 19 | "directory": "packages/brightness" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "tests" 24 | }, 25 | "main": "build/commonjs/index.js", 26 | "react-native": "src/index.ts", 27 | "module": "build/module/index.js", 28 | "types": "build/typescript/src/index.d.ts", 29 | "files": [ 30 | "build", 31 | "src" 32 | ], 33 | "scripts": { 34 | "prepare": "bob build", 35 | "build": "bob build" 36 | }, 37 | "devDependencies": { 38 | "@react-native-community/bob": "^0.14.3", 39 | "@testing-library/react-hooks": "^3.2.1", 40 | "@types/react": "~16.9.32", 41 | "expo-brightness": "~8.1.0", 42 | "react": "16.9.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "peerDependencies": { 46 | "expo-brightness": ">=8.1.0" 47 | }, 48 | "@react-native-community/bob": { 49 | "source": "src", 50 | "output": "build", 51 | "targets": [ 52 | "commonjs", 53 | "module", 54 | "typescript" 55 | ] 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/brightness/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-brightness'; 2 | export * from './use-system-brightness'; 3 | export * from './use-system-brightness-mode'; 4 | -------------------------------------------------------------------------------- /packages/brightness/src/use-brightness.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getBrightnessAsync, setBrightnessAsync } from 'expo-brightness'; 3 | 4 | /** 5 | * Track, set, or get the brightness of the device for the current app. 6 | * It returns a number between `0..1`, which represents the brightness of the screen. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/brightness/#brightnessgetbrightnessasync 9 | * @remarks This does not change the system brightness, and will revert when exiting the app. 10 | * @example const [brightness, setBrightness, getBrightness] = useBrightness(...); 11 | */ 12 | export function useBrightness( 13 | options: BrightnessOptions = {}, 14 | ): [ 15 | number | undefined, 16 | (brightness: number) => Promise, 17 | () => Promise, 18 | ] { 19 | const [data, setData] = useState(); 20 | const { get = true } = options; 21 | 22 | const setBrightness = useCallback((brightness: number) => ( 23 | setBrightnessAsync(brightness).then(() => setData(brightness)) 24 | ), [setData]); 25 | 26 | const getBrightness = useCallback(() => ( 27 | getBrightnessAsync().then(setData) 28 | ), [setData]); 29 | 30 | useEffect(() => { 31 | if (get) { 32 | getBrightness(); 33 | } 34 | }, [get, getBrightness]); 35 | 36 | return [data, setBrightness, getBrightness]; 37 | } 38 | 39 | export interface BrightnessOptions { 40 | /** If it should fetch the brightness when mounted, defaults to `true` */ 41 | get?: boolean; 42 | } 43 | -------------------------------------------------------------------------------- /packages/brightness/src/use-system-brightness-mode.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { BrightnessMode, getSystemBrightnessModeAsync, setSystemBrightnessModeAsync } from 'expo-brightness'; 3 | 4 | /** 5 | * Track, set, or get the (global) system brightness mode of the device. 6 | * It returns the `BrightnessMode` enum with one of the following values: 7 | * - 0 `UNKNOWN` current brightness mode cannot be determined 8 | * - 1 `AUTOMATIC` device OS will automatically adjust the screen brightness depending on the ambient light 9 | * - 2 `MANUAL` device OS won't adjust screen brightness and will remain constant 10 | * 11 | * @see https://docs.expo.io/versions/latest/sdk/brightness/#brightnessgetsystembrightnessmodeasync 12 | * @remarks The set callback requires the `SYSTEM_BRIGHTNESS` permission. 13 | * @example const [brightnessMode, setBrightnessMode, getBrightnessMode] = useSystemBrightnessMode(...); 14 | */ 15 | export function useSystemBrightnessMode( 16 | options: SystemBrightnessModeOptions = {} 17 | ): [ 18 | BrightnessMode | undefined, 19 | (brightness: BrightnessMode) => Promise, 20 | () => Promise, 21 | ] { 22 | const [data, setData] = useState(); 23 | const { get = true } = options; 24 | 25 | const setSystemBrightnessMode = useCallback((mode: BrightnessMode) => ( 26 | setSystemBrightnessModeAsync(mode).then(() => setData(mode)) 27 | ), []); 28 | 29 | const getSystemBrightnessMode = useCallback(() => ( 30 | getSystemBrightnessModeAsync().then(setData) 31 | ), [setData]); 32 | 33 | useEffect(() => { 34 | if (get) { 35 | getSystemBrightnessMode(); 36 | } 37 | }, [get, getSystemBrightnessMode]); 38 | 39 | return [data, setSystemBrightnessMode, getSystemBrightnessMode]; 40 | } 41 | 42 | export interface SystemBrightnessModeOptions { 43 | /** If it should fetch the brightness mode when mounted, defaults to `true` */ 44 | get?: boolean; 45 | } 46 | -------------------------------------------------------------------------------- /packages/brightness/src/use-system-brightness.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { getSystemBrightnessAsync, setSystemBrightnessAsync } from 'expo-brightness'; 3 | 4 | /** 5 | * Track, set, or get the (global) system brightness of the device. 6 | * It returns a number between `0..1`, which represents the brightness of the screen. 7 | * 8 | * Changing this will modify the (global) system brightness. 9 | * When the user exits the app, the brightness doesn't change. 10 | * 11 | * @see https://docs.expo.io/versions/latest/sdk/brightness/#brightnessgetsystembrightnessasync 12 | * @remarks The set callback requires the `SYSTEM_BRIGHTNESS` permission. 13 | * @remarks This changes the system brightness, and will remain when exiting the app. 14 | * @example const [brightness, setBrightness, getBrightness] = useSystemBrightness(...); 15 | */ 16 | export function useSystemBrightness( 17 | options: SystemBrightnessOptions = {} 18 | ): [ 19 | number | undefined, 20 | (brightness: number) => Promise, 21 | () => Promise, 22 | ] { 23 | const [data, setData] = useState(); 24 | const { get = true } = options; 25 | 26 | const setSystemBrightness = useCallback((brightness: number) => ( 27 | setSystemBrightnessAsync(brightness).then(() => setData(brightness)) 28 | ), [setData]); 29 | 30 | const getSystemBrightness = useCallback(() => ( 31 | getSystemBrightnessAsync().then(setData) 32 | ), [setData]); 33 | 34 | useEffect(() => { 35 | if (get) { 36 | getSystemBrightness(); 37 | } 38 | }, [get, getSystemBrightness]); 39 | 40 | return [data, setSystemBrightness, getSystemBrightness]; 41 | } 42 | 43 | export interface SystemBrightnessOptions { 44 | /** If it should fetch the system brightness when mounted, defaults to `true` */ 45 | get?: boolean; 46 | } 47 | -------------------------------------------------------------------------------- /packages/brightness/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/font/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Code Refactoring 10 | 11 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 17 | 18 | * refactor(battery): upgrade to expo sdk 37 19 | 20 | * refactor(brightness): upgrade to expo sdk 37 21 | 22 | * refactor(font): upgrade to expo sdk 37 23 | 24 | * refactor(permissions): upgrade to expo sdk 37 25 | 26 | * refactor(sensors): upgrade to expo sdk 37 27 | 28 | * refactor: upgrade monorepo to expo sdk 37 29 | 30 | * fix: use compatible semver range for react types 31 | 32 | 33 | 34 | 35 | 36 | ## [1.0.1](https://github.com/bycedric/use-expo/compare/1.0.0...1.0.1) (2020-02-25) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **font:** avoid render loop when using inline fontmaps ([#128](https://github.com/bycedric/use-expo/issues/128)) ([a2f6936](https://github.com/bycedric/use-expo/commit/a2f6936f523404c15cdd9f4a15e911bbcea37b94)) 42 | 43 | 44 | 45 | 46 | 47 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 48 | 49 | 50 | ### Bug Fixes 51 | 52 | * **font:** reinitialize fonts in map after initial render ([#50](https://github.com/bycedric/use-expo/issues/50)) ([a249b19](https://github.com/bycedric/use-expo/commit/a249b1985a1251d46de78eed873dca8acd477a19)) 53 | -------------------------------------------------------------------------------- /packages/font/README.md: -------------------------------------------------------------------------------- 1 |
2 |

font hooks

3 |

Complementary hooks for Expo Font

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/font expo-font
25 |
26 |
27 | 28 | - [`useFonts`](./docs/use-fonts.md)  —  load a map of fonts 29 | 30 |
31 |
32 | with :heart: byCedric 33 |
34 |
35 | -------------------------------------------------------------------------------- /packages/font/docs/use-fonts.md: -------------------------------------------------------------------------------- 1 |
2 |

useFonts

3 |

Load a map of fonts with Font

4 | 5 | 6 | releases 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/font expo-font
25 |
26 |
27 | 28 | ## Usage 29 | 30 | ```jsx 31 | // full hook 32 | const [isLoaded] = useFonts({ ... }); 33 | ``` 34 | 35 | 36 | ## Example 37 | 38 | ```jsx 39 | import { useFonts } from '@use-expo/font'; 40 | import { AppLoading } from 'expo'; 41 | import { Text, View } from 'react-native'; 42 | 43 | const customFonts = { 44 | 'OpenSans-Regular': require('./assets/fonts/open-sans-regular.ttf'), 45 | }; 46 | 47 | function FontExample() { 48 | const [isLoaded] = useFonts(customFonts); 49 | 50 | if (!isLoaded) { 51 | return ; 52 | } 53 | 54 | return ( 55 | 56 | Custom font 57 | 58 | ); 59 | } 60 | ``` 61 | 62 | 63 | ## API 64 | 65 | ```ts 66 | import { FontSource } from 'expo-font'; 67 | 68 | function useFonts(map: FontMap): Result; 69 | 70 | interface FontMap { 71 | /** All fonts, by name, to load */ 72 | [name: string]: FontSource; 73 | } 74 | 75 | type Result = [ 76 | /** If the fonts are loaded or not */ 77 | boolean, 78 | ]; 79 | ``` 80 | 81 |
82 |
83 | with :heart: byCedric 84 |
85 |
86 | -------------------------------------------------------------------------------- /packages/font/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/font", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo-font", 5 | "keywords": [ 6 | "expo", 7 | "font", 8 | "hooks" 9 | ], 10 | "license": "MIT", 11 | "author": "Cedric van Putten (https://bycedric.com)", 12 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/font#readme", 13 | "bugs": { 14 | "url": "https://github.com/bycedric/use-expo/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/use-expo.git", 19 | "directory": "packages/font" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "tests" 24 | }, 25 | "main": "build/commonjs/index.js", 26 | "react-native": "src/index.ts", 27 | "module": "build/module/index.js", 28 | "types": "build/typescript/src/index.d.ts", 29 | "files": [ 30 | "build", 31 | "src" 32 | ], 33 | "scripts": { 34 | "prepare": "bob build", 35 | "build": "bob build" 36 | }, 37 | "devDependencies": { 38 | "@react-native-community/bob": "^0.14.3", 39 | "@testing-library/react-hooks": "^3.2.1", 40 | "@types/react": "~16.9.32", 41 | "expo-font": "~8.1.0", 42 | "react": "16.9.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "peerDependencies": { 46 | "expo-font": ">=8.1.0" 47 | }, 48 | "@react-native-community/bob": { 49 | "source": "src", 50 | "output": "build", 51 | "targets": [ 52 | "commonjs", 53 | "module", 54 | "typescript" 55 | ] 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/font/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fonts'; 2 | -------------------------------------------------------------------------------- /packages/font/src/use-fonts.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { FontSource, loadAsync } from 'expo-font'; 3 | 4 | /** 5 | * Load a map of custom fonts to use in textual elements. 6 | * The map keys are used as font names, and can be used with `fontFamily: ;`. 7 | * It returns a boolean describing if all fonts are loaded. 8 | * 9 | * Note, the fonts are not "reloaded" when you dynamically change the font map. 10 | * 11 | * @see https://docs.expo.io/versions/latest/sdk/font/ 12 | * @example const [isLoaded] = useFonts(...); 13 | */ 14 | export function useFonts(map: FontMap): [boolean] { 15 | const [loaded, setLoaded] = useState(false); 16 | 17 | useEffect(() => { 18 | loadAsync(map).then(() => setLoaded(true)) 19 | }, []); // eslint-disable-line 20 | 21 | // note: to avoid any ambiguity fonts are only loaded once 22 | // since every rerender is a new object, we have no way of 23 | // detecting a new map and updating the loaded state based on that 24 | 25 | return [loaded]; 26 | } 27 | 28 | interface FontMap { 29 | [name: string]: FontSource; 30 | } 31 | -------------------------------------------------------------------------------- /packages/font/tests/assets/comic-sans-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/packages/font/tests/assets/comic-sans-regular.ttf -------------------------------------------------------------------------------- /packages/font/tests/assets/open-sans-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byCedric/use-expo/595646f258153916a9b342940283bc7a7c9e50d0/packages/font/tests/assets/open-sans-regular.ttf -------------------------------------------------------------------------------- /packages/font/tests/use-fonts.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks'; 2 | import * as Font from 'expo-font'; 3 | import { useFonts } from '../src/use-fonts'; 4 | 5 | const DATA = 0; 6 | const FONTS = { 7 | 'OpenSans-Regular': require('./assets/open-sans-regular.ttf'), 8 | 'ComicSans-Regular': require('./assets/comic-sans-regular.ttf'), 9 | }; 10 | 11 | it('loads fonts when mounted', async () => { 12 | jest.spyOn(Font, 'loadAsync').mockResolvedValue(); 13 | 14 | const hook = renderHook(() => useFonts(FONTS)); 15 | 16 | expect(hook.result.current[DATA]).toBe(false); 17 | 18 | await hook.waitForNextUpdate(); 19 | 20 | expect(hook.result.current[DATA]).toBe(true); 21 | }); 22 | 23 | it('skips new font map when rerendered', async () => { 24 | const loader = jest.spyOn(Font, 'loadAsync').mockResolvedValue(); 25 | 26 | const hook = renderHook(useFonts, { initialProps: FONTS }); 27 | await hook.waitForNextUpdate(); 28 | 29 | expect(loader).toBeCalledWith(FONTS); 30 | 31 | const partialFonts = { ...FONTS }; 32 | delete partialFonts['ComicSans-Regular']; 33 | 34 | hook.rerender(partialFonts); 35 | 36 | expect(hook.result.current[DATA]).toBe(true); 37 | expect(loader).not.toBeCalledWith(partialFonts); 38 | }); 39 | 40 | it('keeps fonts loaded when unmounted', async () => { 41 | jest.spyOn(Font, 'loadAsync').mockResolvedValue(); 42 | 43 | const hook = renderHook(useFonts, { initialProps: FONTS }); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(true); 47 | 48 | hook.unmount(); 49 | 50 | expect(hook.result.current[DATA]).toBe(true); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/font/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/permissions/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Code Refactoring 10 | 11 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 17 | 18 | * refactor(battery): upgrade to expo sdk 37 19 | 20 | * refactor(brightness): upgrade to expo sdk 37 21 | 22 | * refactor(font): upgrade to expo sdk 37 23 | 24 | * refactor(permissions): upgrade to expo sdk 37 25 | 26 | * refactor(sensors): upgrade to expo sdk 37 27 | 28 | * refactor: upgrade monorepo to expo sdk 37 29 | 30 | * fix: use compatible semver range for react types 31 | 32 | 33 | 34 | 35 | 36 | ## [1.0.1](https://github.com/bycedric/use-expo/compare/1.0.0...1.0.1) (2020-02-25) 37 | 38 | **Note:** Version bump only for package @use-expo/permissions 39 | 40 | 41 | 42 | 43 | 44 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **permissions:** use callbacks and dependencies ([#51](https://github.com/bycedric/use-expo/issues/51)) ([8e55791](https://github.com/bycedric/use-expo/commit/8e5579133f32981376ec47354d17ca5e1b0b6b32)) 50 | -------------------------------------------------------------------------------- /packages/permissions/README.md: -------------------------------------------------------------------------------- 1 |
2 |

permission hooks

3 |

Complementary hooks for Expo Permissions

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/permissions expo-permissions
25 |
26 |
27 | 28 | - [`usePermissions`](./docs/use-permissions.md)  —  get or ask permissions 29 | 30 |
31 |
32 | with :heart: byCedric 33 |
34 |
35 | -------------------------------------------------------------------------------- /packages/permissions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/permissions", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo-permissions", 5 | "keywords": [ 6 | "expo", 7 | "permissions", 8 | "hooks" 9 | ], 10 | "license": "MIT", 11 | "author": "Cedric van Putten (https://bycedric.com)", 12 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/permissions#readme", 13 | "bugs": { 14 | "url": "https://github.com/bycedric/use-expo/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/use-expo.git", 19 | "directory": "packages/permissions" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "tests" 24 | }, 25 | "main": "build/commonjs/index.js", 26 | "react-native": "src/index.ts", 27 | "module": "build/module/index.js", 28 | "types": "build/typescript/src/index.d.ts", 29 | "files": [ 30 | "build", 31 | "src" 32 | ], 33 | "scripts": { 34 | "prepare": "bob build", 35 | "build": "bob build" 36 | }, 37 | "devDependencies": { 38 | "@react-native-community/bob": "^0.14.3", 39 | "@testing-library/react-hooks": "^3.2.1", 40 | "@types/react": "~16.9.32", 41 | "expo-permissions": "~8.1.0", 42 | "react": "16.9.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "peerDependencies": { 46 | "expo-permissions": ">=8.1.0" 47 | }, 48 | "@react-native-community/bob": { 49 | "source": "src", 50 | "output": "build", 51 | "targets": [ 52 | "commonjs", 53 | "module", 54 | "typescript" 55 | ] 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/permissions/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-permissions'; 2 | -------------------------------------------------------------------------------- /packages/permissions/src/use-permissions.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { PermissionType, PermissionResponse, askAsync, getAsync } from 'expo-permissions'; 3 | 4 | /** 5 | * Get or ask permission for protected functionality within the app. 6 | * It returns the permission response after fetching or asking it. 7 | * The hook fetches the permissions when rendered, by default. 8 | * To ask the user permission, use the `askPermission` callback or `ask` option. 9 | * 10 | * @see https://docs.expo.io/versions/latest/sdk/permissions/ 11 | * @example const [permission, askPermission, getPermission] = usePermissions(...); 12 | */ 13 | export function usePermissions( 14 | type: PermissionType | PermissionType[], 15 | options: PermissionsOptions = {}, 16 | ): [ 17 | PermissionResponse | undefined, 18 | () => Promise, 19 | () => Promise, 20 | ] { 21 | const [data, setData] = useState(); 22 | const types = Array.isArray(type) ? type : [type]; 23 | const { 24 | ask = false, 25 | get = true, 26 | } = options; 27 | 28 | // note: its intentional to listen to `type`, not `types`. 29 | // when `type` is casted to an array, it possible creates a new one on every render. 30 | // to prevent unnecessary function instances we need to listen to the "raw" value. 31 | 32 | const askPermissions = useCallback(() => ( 33 | askAsync(...types).then(setData) 34 | ), [type]); // eslint-disable-line react-hooks/exhaustive-deps 35 | 36 | const getPermissions = useCallback(() => ( 37 | getAsync(...types).then(setData) 38 | ), [type]); // eslint-disable-line react-hooks/exhaustive-deps 39 | 40 | useEffect(() => { 41 | if (ask) { 42 | askPermissions(); 43 | } 44 | 45 | if (!ask && get) { 46 | getPermissions(); 47 | } 48 | }, [ask, askPermissions, get, getPermissions]); 49 | 50 | return [data, askPermissions, getPermissions]; 51 | } 52 | 53 | export interface PermissionsOptions { 54 | /** If it should ask the permissions when mounted, defaults to `false` */ 55 | ask?: boolean; 56 | /** If it should fetch information about the permissions when mounted, defaults to `true` */ 57 | get?: boolean; 58 | } 59 | -------------------------------------------------------------------------------- /packages/permissions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/screen-orientation/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Code Refactoring 10 | 11 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 17 | 18 | * refactor(battery): upgrade to expo sdk 37 19 | 20 | * refactor(brightness): upgrade to expo sdk 37 21 | 22 | * refactor(font): upgrade to expo sdk 37 23 | 24 | * refactor(permissions): upgrade to expo sdk 37 25 | 26 | * refactor(sensors): upgrade to expo sdk 37 27 | 28 | * refactor: upgrade monorepo to expo sdk 37 29 | 30 | * fix: use compatible semver range for react types 31 | 32 | 33 | 34 | 35 | 36 | ## [1.0.1](https://github.com/bycedric/use-expo/compare/1.0.0...1.0.1) (2020-02-25) 37 | 38 | **Note:** Version bump only for package @use-expo/screen-orientation 39 | 40 | 41 | 42 | 43 | 44 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 45 | 46 | 47 | ### Features 48 | 49 | * **screen-orientation:** add manual callback and listen option ([#52](https://github.com/bycedric/use-expo/issues/52)) ([106f62e](https://github.com/bycedric/use-expo/commit/106f62ec4084ef411357f8f71bd6af7d7e3b5367)) 50 | -------------------------------------------------------------------------------- /packages/screen-orientation/README.md: -------------------------------------------------------------------------------- 1 |
2 |

screen orientation hooks

3 |

Complementary hooks for Expo Screen Orientation

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/screen-orientation
25 |
26 |
27 | 28 | - [`useScreenOrientation`](./docs/use-screen-orientation.md)  —  track changes in screen orientation 29 | - [`useScreenOrientationLock`](./docs/use-screen-orientation-lock.md)  —  lock the screen to an orientation 30 | 31 |
32 |
33 | with :heart: byCedric 34 |
35 |
36 | -------------------------------------------------------------------------------- /packages/screen-orientation/docs/use-screen-orientation-lock.md: -------------------------------------------------------------------------------- 1 |
2 |

useScreenOrientationLock

3 |

Lock the screen to an orientation with ScreenOrientation

4 | 5 | 6 | releases 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/screen-orientation
25 |
26 |
27 | 28 | ## Usage 29 | 30 | ```jsx 31 | // full hook 32 | const [lockInfo, lockError] = useScreenOrientationLock(OrientationLock.LANDSCAPE_LEFT); 33 | ``` 34 | 35 | 36 | ## Example 37 | 38 | ```jsx 39 | import { OrientationLock } from 'expo-screen-orientation'; 40 | import { useScreenOrientationLock } from '@use-expo/screen-orientation'; 41 | import { Text, View } from 'react-native'; 42 | 43 | function ScreenOrientationLockExample() { 44 | const [lockInfo, lockError] = useScreenOrientationLock(OrientationLock.PORTRAIT); 45 | 46 | return ( 47 | 48 | {lockError 49 | ? Could not lock the screen to portrait mode 50 | : This screen is now locked to portrait mode 51 | } 52 | 53 | ); 54 | } 55 | ``` 56 | 57 | 58 | ## API 59 | 60 | ```ts 61 | import { OrientationLock } from 'expo-screen-orientation'; 62 | 63 | function useScreenOrientationLock(orientation?: OrientationLock): Result; 64 | 65 | type Result = [ 66 | OrientationLock | undefined, 67 | Error | undefined, 68 | ]; 69 | ``` 70 | 71 |
72 |
73 | with :heart: byCedric 74 |
75 |
76 | -------------------------------------------------------------------------------- /packages/screen-orientation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/screen-orientation", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo's screen orientation", 5 | "keywords": [ 6 | "expo", 7 | "screen", 8 | "orientation", 9 | "hooks" 10 | ], 11 | "license": "MIT", 12 | "author": "Cedric van Putten (https://bycedric.com)", 13 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/screen-orientation#readme", 14 | "bugs": { 15 | "url": "https://github.com/bycedric/use-expo/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/bycedric/use-expo.git", 20 | "directory": "packages/screen-orientation" 21 | }, 22 | "directories": { 23 | "lib": "src", 24 | "test": "tests" 25 | }, 26 | "main": "build/commonjs/index.js", 27 | "react-native": "src/index.ts", 28 | "module": "build/module/index.js", 29 | "types": "build/typescript/src/index.d.ts", 30 | "files": [ 31 | "build", 32 | "src" 33 | ], 34 | "scripts": { 35 | "prepare": "bob build", 36 | "build": "bob build" 37 | }, 38 | "devDependencies": { 39 | "@react-native-community/bob": "^0.14.3", 40 | "@testing-library/react-hooks": "^3.2.1", 41 | "@types/react": "~16.9.32", 42 | "expo-screen-orientation": "~1.0.0", 43 | "react": "16.9.0", 44 | "typescript": "^3.8.3" 45 | }, 46 | "peerDependencies": { 47 | "expo-screen-orientation": ">=1.0.0" 48 | }, 49 | "@react-native-community/bob": { 50 | "source": "src", 51 | "output": "build", 52 | "targets": [ 53 | "commonjs", 54 | "module", 55 | "typescript" 56 | ] 57 | }, 58 | "publishConfig": { 59 | "access": "public" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/screen-orientation/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-screen-orientation'; 2 | export * from './use-screen-orientation-lock'; 3 | export * from './use-screen-orientation-platform-lock'; 4 | -------------------------------------------------------------------------------- /packages/screen-orientation/src/use-screen-orientation-lock.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import * as ScreenOrientation from 'expo-screen-orientation'; 3 | 4 | /** 5 | * Get or lock the screen orientation of the device. 6 | * This hook will always fetch the current device lock. 7 | * It will also apply the device lock when this is provided. 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/screen-orientation/ 10 | * @example const [lockInfo, lockError] = useScreenOrientationLock(...); 11 | */ 12 | export function useScreenOrientationLock( 13 | lock?: ScreenOrientation.OrientationLock, 14 | ): [ 15 | ScreenOrientation.OrientationLock | undefined, 16 | Error | undefined, 17 | ] { 18 | const [lockInfo, setLockInfo] = useState(); 19 | const [lockError, setLockError] = useState(); 20 | 21 | useEffect(() => { 22 | ScreenOrientation.getOrientationLockAsync().then(setLockInfo); 23 | 24 | if (lock) { 25 | ScreenOrientation 26 | .lockAsync(lock) 27 | .then(() => setLockInfo(lock)) 28 | .catch(setLockError); 29 | 30 | return () => { 31 | ScreenOrientation.unlockAsync(); 32 | }; 33 | } 34 | }, [lock]); 35 | 36 | return [lockInfo, lockError]; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /packages/screen-orientation/src/use-screen-orientation-platform-lock.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import * as ScreenOrientation from 'expo-screen-orientation' 3 | 4 | /** 5 | * Get or lock the screen orientation on multiple platforms. 6 | * This hook will always fetch the current platform lock. 7 | * It will also apply the platform lock when this is provided. 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/screen-orientation/ 10 | * @example const [lockInfo, lockError] = useScreenOrientationPlatformLock(...); 11 | */ 12 | export function useScreenOrientationPlatformLock( 13 | platformInfo?: ScreenOrientation.PlatformOrientationInfo, 14 | ): [ 15 | ScreenOrientation.PlatformOrientationInfo | undefined, 16 | Error | undefined, 17 | ] { 18 | const [platformLock, setPlatformLock] = useState(); 19 | const [platformLockError, setPlatformLockError] = useState(); 20 | 21 | useEffect(() => { 22 | ScreenOrientation.getPlatformOrientationLockAsync().then(setPlatformLock); 23 | 24 | if (platformInfo) { 25 | ScreenOrientation 26 | .lockPlatformAsync(platformInfo) 27 | .then(() => setPlatformLock(platformInfo)) 28 | .catch(setPlatformLockError); 29 | 30 | return () => { 31 | ScreenOrientation.unlockAsync(); 32 | }; 33 | } 34 | }, [platformInfo]); 35 | 36 | return [platformLock, platformLockError]; 37 | } 38 | -------------------------------------------------------------------------------- /packages/screen-orientation/src/use-screen-orientation.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import * as ScreenOrientation from 'expo-screen-orientation'; 3 | 4 | /** 5 | * Get or track the screen orientation of the device. 6 | * It returns one of the `Orientation` enums, which represents the current orientation. 7 | * For iOS, it also returns both the horizontal and vertical size classes. 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/screen-orientation/ 10 | * @example const [orientation, getOrientation] = useScreenOrientation(...); 11 | */ 12 | export function useScreenOrientation( 13 | options: ScreenOrientationOptions = {}, 14 | ): [ 15 | ScreenOrientation.ScreenOrientationInfo | undefined, 16 | () => Promise, 17 | ] { 18 | const [orientation, setOrientation] = useState(); 19 | const { 20 | get = true, 21 | listen = true, 22 | } = options; 23 | 24 | const getOrientation = useCallback(() => ( 25 | ScreenOrientation.getOrientationAsync().then(orientation => { 26 | setOrientation({ orientation }); 27 | return orientation; 28 | }) 29 | ), []); 30 | 31 | useEffect(() => { 32 | if (get) { 33 | getOrientation(); 34 | } 35 | 36 | if (listen) { 37 | const subscription = ScreenOrientation.addOrientationChangeListener( 38 | event => setOrientation(event.orientationInfo), 39 | ); 40 | 41 | return subscription.remove; 42 | } 43 | }, [get, listen, getOrientation]); 44 | 45 | return [orientation, getOrientation]; 46 | } 47 | 48 | export interface ScreenOrientationOptions { 49 | /** If it should fetch the screen orientation when mounted, defaults to `true` */ 50 | get?: boolean; 51 | /** If it should listen to screen orientation changes, defaults to `true` */ 52 | listen?: boolean; 53 | } 54 | -------------------------------------------------------------------------------- /packages/screen-orientation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sensors/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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Code Refactoring 10 | 11 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 17 | 18 | * refactor(battery): upgrade to expo sdk 37 19 | 20 | * refactor(brightness): upgrade to expo sdk 37 21 | 22 | * refactor(font): upgrade to expo sdk 37 23 | 24 | * refactor(permissions): upgrade to expo sdk 37 25 | 26 | * refactor(sensors): upgrade to expo sdk 37 27 | 28 | * refactor: upgrade monorepo to expo sdk 37 29 | 30 | * fix: use compatible semver range for react types 31 | 32 | 33 | 34 | 35 | 36 | ## [1.0.1](https://github.com/bycedric/use-expo/compare/1.0.0...1.0.1) (2020-02-25) 37 | 38 | **Note:** Version bump only for package @use-expo/sensors 39 | 40 | 41 | 42 | 43 | 44 | # [1.0.0](https://github.com/bycedric/use-expo/compare/v0.10.1...1.0.0) (2020-01-23) 45 | 46 | **Note:** Version bump only for package @use-expo/sensors 47 | -------------------------------------------------------------------------------- /packages/sensors/README.md: -------------------------------------------------------------------------------- 1 |
2 |

sensor hooks

3 |

Complementary hooks for Expo Sensors

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/sensors expo-sensors
25 |
26 |
27 | 28 | - [`useAccelerometer`](./docs/use-accelerometer.md)  —  track changes in acceleration 29 | - [`useBarometer`](./docs/use-barometer.md)  —  track changes in air pressure 30 | - [`useDeviceMotion`](./docs/use-device-motion.md)  —  track device motion and orientation 31 | - [`useGyroscope`](./docs/use-gyroscope.md)  —  track changes in rotation 32 | - [`useMagnetometer`](./docs/use-magnetometer.md)  —  track changes in the magnetic field 33 | - [`useMagnetometerUncalibrated`](./docs/use-magnetometer.md)  —  track changes in the magnetic field using raw data 34 | - [`usePedometer`](./docs/use-pedometer.md)  —  track user step count 35 | - [`usePedometerHistory`](./docs/use-pedometer-history.md)  —  get historical step count between two dates 36 | 37 |
38 |
39 | with :heart: byCedric 40 |
41 |
42 | -------------------------------------------------------------------------------- /packages/sensors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/sensors", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo-sensors", 5 | "keywords": [ 6 | "expo", 7 | "sensors", 8 | "hooks" 9 | ], 10 | "license": "MIT", 11 | "author": "Cedric van Putten (https://bycedric.com)", 12 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/sensors#readme", 13 | "bugs": { 14 | "url": "https://github.com/bycedric/use-expo/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/use-expo.git", 19 | "directory": "packages/sensors" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "tests" 24 | }, 25 | "main": "build/commonjs/index.js", 26 | "react-native": "src/index.ts", 27 | "module": "build/module/index.js", 28 | "types": "build/typescript/src/index.d.ts", 29 | "files": [ 30 | "build", 31 | "src" 32 | ], 33 | "scripts": { 34 | "prepare": "bob build", 35 | "build": "bob build" 36 | }, 37 | "devDependencies": { 38 | "@react-native-community/bob": "^0.14.3", 39 | "@testing-library/react-hooks": "^3.2.1", 40 | "@types/react": "~16.9.32", 41 | "expo-sensors": "~8.1.0", 42 | "react": "16.9.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "peerDependencies": { 46 | "expo-sensors": ">=8.1.0" 47 | }, 48 | "@react-native-community/bob": { 49 | "source": "src", 50 | "output": "build", 51 | "targets": [ 52 | "commonjs", 53 | "module", 54 | "typescript" 55 | ] 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/sensors/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-accelerometer'; 2 | export * from './use-barometer'; 3 | export * from './use-device-motion'; 4 | export * from './use-gyroscope'; 5 | export * from './use-magnetometer'; 6 | export * from './use-magnetometer-uncalibrated'; 7 | export * from './use-pedometer'; 8 | export * from './use-pedometer-history'; 9 | -------------------------------------------------------------------------------- /packages/sensors/src/use-accelerometer.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Accelerometer, ThreeAxisMeasurement } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the accelerometer sensor data from the device. 6 | * It returns a `ThreeAxisMeasurement`, with x, y, and z properties. 7 | * Optionally, you can provide the update interval (in ms). 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/accelerometer/ 10 | * @remarks Changing the update interval will affect all accelerometer listeners. 11 | * @example const [data, isAvailable] = useAccelerometer(...); 12 | */ 13 | export function useAccelerometer( 14 | options: AccelerometerOptions = {} 15 | ): [ 16 | ThreeAxisMeasurement | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | interval, 24 | } = options; 25 | 26 | useEffect(() => { 27 | if (availability) { 28 | Accelerometer.isAvailableAsync().then(setAvailable); 29 | } 30 | 31 | if (interval !== undefined) { 32 | Accelerometer.setUpdateInterval(interval); 33 | } 34 | 35 | return Accelerometer.addListener(setData).remove; 36 | }, [availability, interval]); 37 | 38 | return [data, available]; 39 | } 40 | 41 | export interface AccelerometerOptions { 42 | /** The initial data to use before the first update. */ 43 | initial?: ThreeAxisMeasurement; 44 | /** If it should check the availability of the sensor, defaults to `true`. */ 45 | availability?: boolean; 46 | 47 | /** 48 | * The interval, in ms, to update the accelerometer data. 49 | * Note, this is set globally through `Accelerometer.setUpdateInterval`. 50 | * When used in 2 or more components, only the last rendered component's interval will be used for all. 51 | */ 52 | interval?: number; 53 | } 54 | -------------------------------------------------------------------------------- /packages/sensors/src/use-barometer.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Barometer, BarometerMeasurement } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the barometer sensor data from the device. 6 | * It returns the `BarometerMeasurement`, with the pressure and relative altitude (if possible). 7 | * Optionally, you can provide the update interval (in ms). 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/barometer/ 10 | * @remarks Changing the update interval will affect all barometer listeners. 11 | * @example const [data, isAvailable] = useBarometer(...); 12 | */ 13 | export function useBarometer( 14 | options: BarometerOptions = {} 15 | ): [ 16 | BarometerMeasurement | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | interval, 24 | } = options; 25 | 26 | useEffect(() => { 27 | if (availability) { 28 | Barometer.isAvailableAsync().then(setAvailable); 29 | } 30 | 31 | if (interval !== undefined) { 32 | Barometer.setUpdateInterval(interval); 33 | } 34 | 35 | return Barometer.addListener(setData).remove; 36 | }, [availability, interval]); 37 | 38 | return [data, available]; 39 | } 40 | 41 | export interface BarometerOptions { 42 | /** The initial data to use before the first update. */ 43 | initial?: BarometerMeasurement; 44 | /** If it should check the availability of the sensor, defaults to `true`. */ 45 | availability?: boolean; 46 | 47 | /** 48 | * The interval, in ms, to update the barometer data. 49 | * Note, this is set globally through `Barometer.setUpdateInterval`. 50 | * When used in 2 or more components, only the last rendered component's interval will be used for all. 51 | */ 52 | interval?: number; 53 | } 54 | -------------------------------------------------------------------------------- /packages/sensors/src/use-device-motion.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { DeviceMotion, DeviceMotionMeasurement } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the motion of the device using multiple sensors. 6 | * It returns a `DeviceMotionMeasurement`, with acceleration, rotation, and orientation. 7 | * Optionally, you can provide the update interval (in ms). 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/devicemotion/ 10 | * @remarks Changing the update interval will affect all device motion listeners. 11 | * @example const [data, isAvailable] = useDeviceMotion(...); 12 | */ 13 | export function useDeviceMotion( 14 | options: DeviceMotionOptions = {} 15 | ): [ 16 | DeviceMotionMeasurement | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | interval, 24 | } = options; 25 | 26 | useEffect(() => { 27 | if (availability) { 28 | DeviceMotion.isAvailableAsync().then(setAvailable); 29 | } 30 | 31 | if (interval !== undefined) { 32 | DeviceMotion.setUpdateInterval(interval); 33 | } 34 | 35 | return DeviceMotion.addListener(setData).remove; 36 | }, [availability, interval]); 37 | 38 | return [data, available]; 39 | } 40 | 41 | export interface DeviceMotionOptions { 42 | /** The initial data to use before the first update. */ 43 | initial?: DeviceMotionMeasurement; 44 | /** If it should check the availability of the sensor, defaults to `true`. */ 45 | availability?: boolean; 46 | 47 | /** 48 | * The interval, in ms, to update the device motion data. 49 | * Note, this is set globally through `DeviceMotion.setUpdateInterval`. 50 | * When used in 2 or more components, only the last rendered component's interval will be used for all. 51 | */ 52 | interval?: number; 53 | } 54 | -------------------------------------------------------------------------------- /packages/sensors/src/use-gyroscope.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Gyroscope, ThreeAxisMeasurement } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the gyroscope sensor data from the device. 6 | * It returns a `ThreeAxisMeasurement`, with x, y, and z properties. 7 | * Optionally, you can provide the update interval (in ms). 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/gyroscope/ 10 | * @remarks Changing the update interval will affect all gyroscope listeners. 11 | * @example const [data, isAvailable] = useGyroscope(...); 12 | */ 13 | export function useGyroscope( 14 | options: GyroscopeOptions = {} 15 | ): [ 16 | ThreeAxisMeasurement | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | interval, 24 | } = options; 25 | 26 | useEffect(() => { 27 | if (availability) { 28 | Gyroscope.isAvailableAsync().then(setAvailable); 29 | } 30 | 31 | if (interval !== undefined) { 32 | Gyroscope.setUpdateInterval(interval); 33 | } 34 | 35 | return Gyroscope.addListener(setData).remove; 36 | }, [availability, interval]); 37 | 38 | return [data, available]; 39 | } 40 | 41 | export interface GyroscopeOptions { 42 | /** The initial data to use before the first update. */ 43 | initial?: ThreeAxisMeasurement; 44 | /** If it should check the availability of the sensor, defaults to `true`. */ 45 | availability?: boolean; 46 | 47 | /** 48 | * The interval, in ms, to update the gyroscope data. 49 | * Note, this is set globally through `Gyroscope.setUpdateInterval`. 50 | * When used in 2 or more components, only the last rendered component's interval will be used for all. 51 | */ 52 | interval?: number; 53 | } 54 | -------------------------------------------------------------------------------- /packages/sensors/src/use-magnetometer-uncalibrated.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { MagnetometerUncalibrated, ThreeAxisMeasurement } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the (uncalibrated) magnetometer sensor data from the device. 6 | * It returns a `ThreeAxisMeasurement`, with x, y, and z properties. 7 | * Optionally, you can provide the update interval (in ms). 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/magnetometer/ 10 | * @remarks Changing the update interval will affect all uncalibrated magnetometer listeners. 11 | * @example const [data, isAvailable] = useMagnetometerUncalibrated(...); 12 | */ 13 | export function useMagnetometerUncalibrated( 14 | options: MagnetometerUncalibratedOptions = {} 15 | ): [ 16 | ThreeAxisMeasurement | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | interval, 24 | } = options; 25 | 26 | useEffect(() => { 27 | if (availability) { 28 | MagnetometerUncalibrated.isAvailableAsync().then(setAvailable); 29 | } 30 | 31 | if (interval !== undefined) { 32 | MagnetometerUncalibrated.setUpdateInterval(interval); 33 | } 34 | 35 | return MagnetometerUncalibrated.addListener(setData).remove; 36 | }, [availability, interval]); 37 | 38 | return [data, available]; 39 | } 40 | 41 | export interface MagnetometerUncalibratedOptions { 42 | /** The initial data to use before the first update. */ 43 | initial?: ThreeAxisMeasurement; 44 | /** If it should check the availability of the sensor, defaults to `true`. */ 45 | availability?: boolean; 46 | 47 | /** 48 | * The interval, in ms, to update the uncalibrated magnetometer data. 49 | * Note, this is set globally through `MagnetometerUncalibrated.setUpdateInterval`. 50 | * When used in 2 or more components, only the last rendered component's interval will be used for all. 51 | */ 52 | interval?: number; 53 | } 54 | -------------------------------------------------------------------------------- /packages/sensors/src/use-magnetometer.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Magnetometer, ThreeAxisMeasurement } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the (calibrated) magnetometer sensor data from the device. 6 | * It returns a `ThreeAxisMeasurement`, with x, y, and z properties. 7 | * Optionally, you can provide the update interval (in ms). 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/magnetometer/ 10 | * @remarks Changing the update interval will affect all magnetometer listeners. 11 | * @example const [data, isAvailable] = useMagnetometer(...); 12 | */ 13 | export function useMagnetometer( 14 | options: MagnetometerOptions = {} 15 | ): [ 16 | ThreeAxisMeasurement | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | interval, 24 | } = options; 25 | 26 | useEffect(() => { 27 | if (availability) { 28 | Magnetometer.isAvailableAsync().then(setAvailable); 29 | } 30 | 31 | if (interval !== undefined) { 32 | Magnetometer.setUpdateInterval(interval); 33 | } 34 | 35 | return Magnetometer.addListener(setData).remove; 36 | }, [availability, interval]); 37 | 38 | return [data, available]; 39 | } 40 | 41 | export interface MagnetometerOptions { 42 | /** The initial data to use before the first update. */ 43 | initial?: ThreeAxisMeasurement; 44 | /** If it should check the availability of the sensor, defaults to `true`. */ 45 | availability?: boolean; 46 | 47 | /** 48 | * The interval, in ms, to update the magnetometer data. 49 | * Note, this is set globally through `Magnetometer.setUpdateInterval`. 50 | * When used in 2 or more components, only the last rendered component's interval will be used for all. 51 | */ 52 | interval?: number; 53 | } 54 | -------------------------------------------------------------------------------- /packages/sensors/src/use-pedometer-history.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Pedometer } from 'expo-sensors'; 3 | import { PedometerResult, PedometerOptions } from './use-pedometer'; 4 | 5 | /** 6 | * Get a historical step count between two dates from the device. 7 | * It returns an object with a `steps` number. 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/pedometer/ 10 | * @remarks iOS only returns the last 7 days. 11 | * @example const [data, isAvailable] = usePedometerHistory(...); 12 | */ 13 | export function usePedometerHistory( 14 | options: PedometerHistpryOptions 15 | ): [ 16 | PedometerResult | undefined, 17 | boolean | undefined, 18 | ] { 19 | const [data, setData] = useState(options.initial); 20 | const [available, setAvailable] = useState(); 21 | const { 22 | availability = true, 23 | start, 24 | end, 25 | } = options; 26 | 27 | useEffect(() => { 28 | if (availability) { 29 | Pedometer.isAvailableAsync().then(setAvailable); 30 | } 31 | 32 | Pedometer.getStepCountAsync(start, end).then(setData); 33 | }, [availability, start, end]); 34 | 35 | return [data, available]; 36 | } 37 | 38 | export interface PedometerHistpryOptions extends PedometerOptions { 39 | /** 40 | * The start of the range to fetch the step count. 41 | * 42 | * @see https://docs.expo.io/versions/latest/sdk/pedometer/#pedometergetstepcountasyncstart-end 43 | */ 44 | start: Date; 45 | 46 | /** 47 | * The end of the range to fetch the step count. 48 | * 49 | * @see https://docs.expo.io/versions/latest/sdk/pedometer/#pedometergetstepcountasyncstart-end 50 | */ 51 | end: Date; 52 | } 53 | -------------------------------------------------------------------------------- /packages/sensors/src/use-pedometer.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Pedometer } from 'expo-sensors'; 3 | 4 | /** 5 | * Track the current step count from the device. 6 | * It returns an object with a `steps` number. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/pedometer/ 9 | * @example const [data, isAvailable] = usePedometer(...); 10 | */ 11 | export function usePedometer( 12 | options: PedometerOptions = {} 13 | ): [ 14 | PedometerResult | undefined, 15 | boolean | undefined, 16 | ] { 17 | const [data, setData] = useState(options.initial); 18 | const [available, setAvailable] = useState(); 19 | const { availability = true } = options; 20 | 21 | useEffect(() => { 22 | if (availability) { 23 | Pedometer.isAvailableAsync().then(setAvailable); 24 | } 25 | 26 | return Pedometer.watchStepCount(setData).remove; 27 | }, [availability]); 28 | 29 | return [data, available]; 30 | } 31 | 32 | // note: this isn't exported from expo, so we need to duplicate it 33 | export interface PedometerResult { 34 | /** The amount of steps made */ 35 | steps: number; 36 | } 37 | 38 | export interface PedometerOptions { 39 | /** The initial data to use before the first update. */ 40 | initial?: PedometerResult; 41 | /** If it should check the availability of the sensor, defaults to `true`. */ 42 | availability?: boolean; 43 | } 44 | -------------------------------------------------------------------------------- /packages/sensors/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /packages/store-review/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 | # 1.0.0 (2020-05-11) 7 | 8 | ### Features 9 | 10 | * **store-review:** add store review hooks ([#253](https://github.com/bycedric/use-expo/issues/253)) ([35efe21](https://github.com/bycedric/use-expo/commit/35efe2158f2518ea5213e543573ee2b913f9f536)) 11 | -------------------------------------------------------------------------------- /packages/store-review/README.md: -------------------------------------------------------------------------------- 1 |
2 |

StoreReview hooks

3 |

Complementary hooks for Expo StoreReview

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/store-review expo-store-review
25 |
26 |
27 | 28 | - [`useStoreReviewHasAction`](./docs/use-store-review-has-action.md)  —  determines if the store review can perform any action 29 | - [`useStoreReviewIsAvailable`](./docs/use-store-review-is-available.md)  —  determines if the platform has the capabilities to use request review 30 | - [`useStoreReviewRequest`](./docs/use-store-review-request.md)  —  request a store review if available 31 | 32 |
33 |
34 | with :heart: byCedric 35 |
36 |
37 | -------------------------------------------------------------------------------- /packages/store-review/docs/use-store-review-request.md: -------------------------------------------------------------------------------- 1 |
2 |

useStoreReviewRequest

3 |

Request a review with StoreReview

4 | 5 | 6 | releases 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
expo install @use-expo/store-review expo-store-review
25 |
26 |
27 | 28 | ## Usage 29 | 30 | ```jsx 31 | // full hook 32 | const requestReview = useStoreReviewRequest(); 33 | ``` 34 | 35 | ## Example 36 | 37 | ```jsx 38 | import { useStoreReviewRequest } from '@use-expo/store-review'; 39 | import React, { useEffect } from 'react'; 40 | import { Text, View } from 'react-native'; 41 | 42 | function StoreReviewRequestExample() { 43 | const requestReview = useStoreReviewRequest(); 44 | 45 | useEffect(() => { 46 | requestReview().then((isRequested) => { 47 | if (isRequested) { 48 | console.log('Store review is requested'); 49 | } else { 50 | console.log('Could not request review (expo web?)'); 51 | } 52 | }); 53 | }, [requestReview]); 54 | 55 | return ( 56 | 57 | Please review our app! 58 | 59 | ); 60 | } 61 | ``` 62 | 63 | ## API 64 | 65 | ```ts 66 | function useStoreReviewRequest(): (Result); 67 | 68 | /** Function to request a review */ 69 | type Result = () => Promise; 70 | ``` 71 | 72 |
73 |
74 | with :heart: byCedric 75 |
76 |
77 | -------------------------------------------------------------------------------- /packages/store-review/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/store-review", 3 | "version": "1.0.0", 4 | "description": "Complementary hooks for expo-store-review", 5 | "keywords": [ 6 | "expo", 7 | "store", 8 | "review", 9 | "hooks" 10 | ], 11 | "license": "MIT", 12 | "author": "Cedric van Putten (https://bycedric.com)", 13 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/store-review#readme", 14 | "bugs": { 15 | "url": "https://github.com/bycedric/use-expo/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/bycedric/use-expo", 20 | "directory": "packages/store-review" 21 | }, 22 | "directories": { 23 | "lib": "src", 24 | "test": "tests" 25 | }, 26 | "main": "build/commonjs/index.js", 27 | "react-native": "src/index.ts", 28 | "module": "build/module/index.js", 29 | "types": "build/typescript/src/index.d.ts", 30 | "files": [ 31 | "build", 32 | "src" 33 | ], 34 | "scripts": { 35 | "prepare": "bob build", 36 | "build": "bob build" 37 | }, 38 | "devDependencies": { 39 | "@react-native-community/bob": "^0.14.3", 40 | "@testing-library/react-hooks": "^3.2.1", 41 | "@types/react": "~16.9.32", 42 | "expo-store-review": "~2.1.0", 43 | "react": "16.9.0", 44 | "typescript": "^3.8.3" 45 | }, 46 | "peerDependencies": { 47 | "expo-store-review": ">=2.1.0" 48 | }, 49 | "@react-native-community/bob": { 50 | "source": "src", 51 | "output": "build", 52 | "targets": [ 53 | "commonjs", 54 | "module", 55 | "typescript" 56 | ] 57 | }, 58 | "publishConfig": { 59 | "access": "public" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/store-review/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-store-review-has-action'; 2 | export * from './use-store-review-is-available'; 3 | export * from './use-store-review-request'; 4 | -------------------------------------------------------------------------------- /packages/store-review/src/use-store-review-has-action.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { hasAction } from 'expo-store-review'; 3 | 4 | /** 5 | * Determines if the store review can perform any action. 6 | * This hook returns `false` when the app manifest doesn't have the store URLs for the platform. 7 | * - On Android, this is the `android.playStoreUrl` field. 8 | * - For iOS, this is the `ios.appStoreUrl` field. 9 | * 10 | * @see https://docs.expo.io/versions/latest/sdk/storereview/#storereviewhasaction 11 | * @example const [hasAction, getHasAction] = useStoreReviewHasAction(...); 12 | */ 13 | export function useStoreReviewHasAction( 14 | options: StoreReviewHasActionOptions = {}, 15 | ): [ 16 | boolean | undefined, 17 | () => Promise, 18 | ] { 19 | const [storeReviewHasAction, setStoreReviewHasAction] = useState(); 20 | const { get = true } = options; 21 | 22 | const getStoreReviewHasAction = useCallback(() => ( 23 | hasAction().then(setStoreReviewHasAction) 24 | ), []); 25 | 26 | useEffect(() => { 27 | if (get) { 28 | getStoreReviewHasAction(); 29 | } 30 | }, [get, getStoreReviewHasAction]); 31 | 32 | return [storeReviewHasAction, getStoreReviewHasAction]; 33 | } 34 | 35 | export interface StoreReviewHasActionOptions { 36 | /** If it should fetch the store review has action when mounted, defaults to `true` */ 37 | get?: boolean; 38 | } 39 | -------------------------------------------------------------------------------- /packages/store-review/src/use-store-review-is-available.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { isAvailableAsync } from 'expo-store-review'; 3 | 4 | /** 5 | * Determines if the platform has the capabilities to use the request review method. 6 | * This returns `true` for Android and iOS 10.3+, on web this returns with `false`. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/storereview/#storereviewisavailableasync 9 | * @example const [isAvailable, getIsAvailable] = useStoreReviewIsAvailable(...); 10 | */ 11 | export function useStoreReviewIsAvailable( 12 | options: StoreReviewIsAvailableOptions = {}, 13 | ): [ 14 | boolean | undefined, 15 | () => Promise, 16 | ] { 17 | const [storeReviewIsAvailable, setStoreReviewIsAvailable] = useState(); 18 | const { get = true } = options; 19 | 20 | const getStoreReviewIsAvailable = useCallback(() => ( 21 | isAvailableAsync().then(setStoreReviewIsAvailable) 22 | ), []); 23 | 24 | useEffect(() => { 25 | if (get) { 26 | getStoreReviewIsAvailable(); 27 | } 28 | }, [get, getStoreReviewIsAvailable]); 29 | 30 | return [storeReviewIsAvailable, getStoreReviewIsAvailable]; 31 | } 32 | 33 | export interface StoreReviewIsAvailableOptions { 34 | /** If it should fetch the store review is available when mounted, defaults to `true` */ 35 | get?: boolean; 36 | } 37 | -------------------------------------------------------------------------------- /packages/store-review/src/use-store-review-request.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import * as StoreReview from 'expo-store-review'; 3 | 4 | /** 5 | * Request a store review if the app manifest has the appropriate fields. 6 | * Ideally, this will open a native modal with the rating form without leaving the app. 7 | * On Android and iOS <10.3, this will link the user to the store URL. 8 | * - On Android, this is the `android.playStoreUrl` field. 9 | * - For iOS, this is the `ios.appStoreUrl` field. 10 | * 11 | * @see https://docs.expo.io/versions/latest/sdk/storereview/#storereviewrequestreview 12 | * @example const requestReview = useStoreReviewRequest(); 13 | */ 14 | export function useStoreReviewRequest(): () => Promise { 15 | return useCallback(async () => { 16 | const hasAction = await StoreReview.hasAction(); 17 | 18 | if (hasAction) { 19 | await StoreReview.requestReview(); 20 | } 21 | 22 | return hasAction; 23 | }, []); 24 | } 25 | -------------------------------------------------------------------------------- /packages/store-review/tests/use-store-review-has-action.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as StoreReview from 'expo-store-review'; 3 | import { useStoreReviewHasAction } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | it('returns data and get callback when mounted', () => { 9 | const hook = renderHook(() => useStoreReviewHasAction({ get: false })); 10 | 11 | expect(hook.result.current[DATA]).toBeUndefined(); 12 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 13 | }); 14 | 15 | describe('get callback', () => { 16 | it('updates data with get callback', async () => { 17 | expect.assertions(2); 18 | 19 | jest.spyOn(StoreReview, 'hasAction').mockResolvedValue(true); 20 | 21 | const hook = renderHook(() => useStoreReviewHasAction({ get: false })); 22 | expect(hook.result.current[DATA]).toBeUndefined(); 23 | await act(() => hook.result.current[GET]()); 24 | 25 | expect(hook.result.current[DATA]).toEqual(true); 26 | }); 27 | 28 | it('uses the same get callback when rerendered', async () => { 29 | const hook = renderHook(() => useStoreReviewHasAction({ get: false })); 30 | const getter = hook.result.current[GET]; 31 | hook.rerender({ get: false }); 32 | 33 | expect(getter).toBe(hook.result.current[GET]); 34 | }); 35 | }); 36 | 37 | describe('get option', () => { 38 | it('gets store review has action when mounted', async () => { 39 | expect.assertions(1); 40 | 41 | jest.spyOn(StoreReview, 'hasAction').mockResolvedValue(true); 42 | 43 | const hook = renderHook(() => useStoreReviewHasAction({ get: true })); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(true); 47 | }); 48 | 49 | it('updates data with get option when rerendered', async () => { 50 | expect.assertions(2); 51 | 52 | jest.spyOn(StoreReview, 'hasAction').mockResolvedValue(true); 53 | 54 | const hook = renderHook(useStoreReviewHasAction, { initialProps: { get: false } }); 55 | expect(hook.result.current[DATA]).toBeUndefined(); 56 | hook.rerender({ get: true }) 57 | await hook.waitForNextUpdate(); 58 | 59 | expect(hook.result.current[DATA]).toBe(true); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /packages/store-review/tests/use-store-review-is-available.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import * as StoreReview from 'expo-store-review'; 3 | import { useStoreReviewIsAvailable } from '../src'; 4 | 5 | const DATA = 0; 6 | const GET = 1; 7 | 8 | it('returns data and get callback when mounted', () => { 9 | const hook = renderHook(() => useStoreReviewIsAvailable({ get: false })); 10 | 11 | expect(hook.result.current[DATA]).toBeUndefined(); 12 | expect(hook.result.current[GET]).toBeInstanceOf(Function); 13 | }); 14 | 15 | describe('get callback', () => { 16 | it('updates data with get callback', async () => { 17 | expect.assertions(2); 18 | 19 | jest.spyOn(StoreReview, 'isAvailableAsync').mockResolvedValue(true); 20 | 21 | const hook = renderHook(() => useStoreReviewIsAvailable({ get: false })); 22 | expect(hook.result.current[DATA]).toBeUndefined(); 23 | await act(() => hook.result.current[GET]()); 24 | 25 | expect(hook.result.current[DATA]).toEqual(true); 26 | }); 27 | 28 | it('uses the same get callback when rerendered', async () => { 29 | const hook = renderHook(() => useStoreReviewIsAvailable({ get: false })); 30 | const getter = hook.result.current[GET]; 31 | hook.rerender({ get: false }); 32 | 33 | expect(getter).toBe(hook.result.current[GET]); 34 | }); 35 | }); 36 | 37 | describe('get option', () => { 38 | it('gets store review is available when mounted', async () => { 39 | expect.assertions(1); 40 | 41 | jest.spyOn(StoreReview, 'isAvailableAsync').mockResolvedValue(true); 42 | 43 | const hook = renderHook(() => useStoreReviewIsAvailable({ get: true })); 44 | await hook.waitForNextUpdate(); 45 | 46 | expect(hook.result.current[DATA]).toBe(true); 47 | }); 48 | 49 | it('updates data with get option when rerendered', async () => { 50 | expect.assertions(2); 51 | 52 | jest.spyOn(StoreReview, 'isAvailableAsync').mockResolvedValue(true); 53 | 54 | const hook = renderHook(useStoreReviewIsAvailable, { initialProps: { get: false } }); 55 | expect(hook.result.current[DATA]).toBeUndefined(); 56 | hook.rerender({ get: true }) 57 | await hook.waitForNextUpdate(); 58 | 59 | expect(hook.result.current[DATA]).toBe(true); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /packages/store-review/tests/use-store-review-request.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks'; 2 | import * as StoreReview from 'expo-store-review'; 3 | import { useStoreReviewRequest } from '../src'; 4 | 5 | it('returns request function', () => { 6 | const hook = renderHook(() => useStoreReviewRequest()); 7 | 8 | expect(hook.result.current).toBeInstanceOf(Function); 9 | }); 10 | 11 | describe('request function', () => { 12 | it('calls request review if available', async () => { 13 | expect.assertions(3); 14 | 15 | const hasActionSpy = jest.spyOn(StoreReview, 'hasAction').mockResolvedValue(true); 16 | const requestReviewSpy = jest.spyOn(StoreReview, 'requestReview').mockResolvedValue(); 17 | 18 | const hook = renderHook(() => useStoreReviewRequest()); 19 | const isRequested = await hook.result.current(); 20 | 21 | expect(hasActionSpy).toHaveBeenCalled(); 22 | expect(requestReviewSpy).toHaveBeenCalled(); 23 | expect(isRequested).toEqual(true); 24 | }); 25 | 26 | it('does not call request review if not available', async () => { 27 | expect.assertions(3); 28 | 29 | const hasActionSpy = jest.spyOn(StoreReview, 'hasAction').mockResolvedValue(false); 30 | const requestReviewSpy = jest.spyOn(StoreReview, 'requestReview').mockResolvedValue(); 31 | 32 | const hook = renderHook(() => useStoreReviewRequest()); 33 | const isRequested = await hook.result.current(); 34 | 35 | expect(hasActionSpy).toHaveBeenCalled(); 36 | expect(requestReviewSpy).not.toHaveBeenCalled(); 37 | expect(isRequested).toEqual(false); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/store-review/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/web-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 | # [2.0.0](https://github.com/bycedric/use-expo/compare/1.0.1...2.0.0) (2020-04-05) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **web-browser:** add tests, fix lint errors and finish docs ([#187](https://github.com/bycedric/use-expo/issues/187)) ([3ddd739](https://github.com/bycedric/use-expo/commit/3ddd73901a1cd2e33e0d7d6c067dd0d88b71f1cf)) 12 | 13 | 14 | ### Code Refactoring 15 | 16 | * upgrade all packages to expo sdk 37 ([#185](https://github.com/bycedric/use-expo/issues/185)) ([083eca2](https://github.com/bycedric/use-expo/commit/083eca28c2271f6581b051e38652e51b4da8bfc9)), closes [#186](https://github.com/bycedric/use-expo/issues/186) 17 | 18 | 19 | ### BREAKING CHANGES 20 | 21 | * this also bumps all peer dependencies to the bundled native modules for SDK 37. 22 | 23 | * refactor(battery): upgrade to expo sdk 37 24 | 25 | * refactor(brightness): upgrade to expo sdk 37 26 | 27 | * refactor(font): upgrade to expo sdk 37 28 | 29 | * refactor(permissions): upgrade to expo sdk 37 30 | 31 | * refactor(sensors): upgrade to expo sdk 37 32 | 33 | * refactor: upgrade monorepo to expo sdk 37 34 | 35 | * fix: use compatible semver range for react types 36 | 37 | 38 | 39 | 40 | 41 | ## Bug Fixes 42 | -------------------------------------------------------------------------------- /packages/web-browser/README.md: -------------------------------------------------------------------------------- 1 |
2 |

web-browser hooks

3 |

Complementary hooks for Expo Web Browser

4 | 5 | 6 | changelog 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
yarn add @use-expo/web-browser expo-web-browser
25 |
expo install @use-expo/web-browser expo-web-browser
26 |
27 |
28 | 29 | - [`useBrowsers`](./docs/use-browsers.md)  —  get a list of valid browser packages on Android. 30 | - [`useWarmBrowser`](./docs/use-warm-browser.md)  —  warm the provided browser. A list of browsers can be found by invoking `WebBrowser.getCustomTabsSupportingBrowsersAsync()`. 31 | 32 |
33 |
34 | with :heart: byCedric 35 |
36 |
37 | -------------------------------------------------------------------------------- /packages/web-browser/docs/use-browsers.md: -------------------------------------------------------------------------------- 1 |
2 |

useBrowsers

3 |

Get a list of Android browsers with WebBrowser

4 | 5 | 6 | releases 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
yarn add @use-expo/web-browser expo-web-browser
25 |
26 |
27 | 28 | ## Usage 29 | 30 | ```jsx 31 | // full hook 32 | const browsers = useBrowsers(); 33 | ``` 34 | 35 | 36 | ## Example 37 | 38 | ```jsx 39 | import { useBrowsers } from '@use-expo/web-browser'; 40 | import { Text, View } from 'react-native'; 41 | 42 | function AndroidBrowsers() { 43 | const browsers = useBrowsers(); 44 | 45 | return ( 46 | 47 | Android Browsers: 48 | {browsers.join(', ')} 49 | 50 | ); 51 | } 52 | ``` 53 | 54 | 55 | ## API 56 | 57 | ```ts 58 | function useBrowsers(): string[]; 59 | ``` 60 | 61 | 62 |
63 |
64 | with :heart: byCedric 65 |
66 |
67 | -------------------------------------------------------------------------------- /packages/web-browser/docs/use-warm-browser.md: -------------------------------------------------------------------------------- 1 |
2 |

useWarmBrowser

3 |

Warm up and cool down and android browser WebBrowser

4 | 5 | 6 | releases 7 | 8 | 9 | builds 10 | 11 | 12 | demo 13 | 14 | 15 |
16 |

17 | Other hooks 18 |   —   19 | Usage 20 |   —   21 | Changelog 22 |

23 |
24 |
yarn add @use-expo/web-browser expo-web-browser
25 |
26 |
27 | 28 | ## Usage 29 | 30 | ```jsx 31 | // full hook 32 | useWarmBrowser('com.android.chrome'); 33 | ``` 34 | 35 | 36 | ## Example 37 | 38 | ```jsx 39 | import { useBrowsers, useWarmBrowser } from '@use-expo/web-browser'; 40 | import { Text, View } from 'react-native'; 41 | 42 | function App() { 43 | const browsers = useBrowsers(); 44 | useWarmBrowser(browsers?.[0]); 45 | 46 | return ; 47 | } 48 | ``` 49 | 50 | 51 | ## API 52 | 53 | ```ts 54 | function useWarmBrowser(browser: string): void; 55 | ``` 56 | 57 |
58 |
59 | with :heart: byCedric 60 |
61 |
62 | -------------------------------------------------------------------------------- /packages/web-browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@use-expo/web-browser", 3 | "version": "2.0.0", 4 | "description": "Complementary hooks for expo-web-browser", 5 | "keywords": [ 6 | "expo", 7 | "web-browser", 8 | "auth", 9 | "hooks" 10 | ], 11 | "license": "MIT", 12 | "author": "Evan Bacon (https://evanbacon.dev)", 13 | "homepage": "https://github.com/bycedric/use-expo/tree/master/packages/web-browser#readme", 14 | "bugs": { 15 | "url": "https://github.com/bycedric/use-expo/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/bycedric/use-expo.git", 20 | "directory": "packages/web-browser" 21 | }, 22 | "directories": { 23 | "lib": "src", 24 | "test": "tests" 25 | }, 26 | "main": "build/commonjs/index.js", 27 | "react-native": "src/index.ts", 28 | "module": "build/module/index.js", 29 | "types": "build/typescript/src/index.d.ts", 30 | "files": [ 31 | "build", 32 | "src" 33 | ], 34 | "scripts": { 35 | "prepare": "bob build", 36 | "build": "bob build" 37 | }, 38 | "devDependencies": { 39 | "@react-native-community/bob": "^0.14.3", 40 | "@testing-library/react-hooks": "^3.2.1", 41 | "@types/react": "~16.9.32", 42 | "expo-web-browser": "~8.2.1", 43 | "react": "16.9.0", 44 | "typescript": "^3.8.3" 45 | }, 46 | "peerDependencies": { 47 | "expo-web-browser": ">=8.1.0" 48 | }, 49 | "@react-native-community/bob": { 50 | "source": "src", 51 | "output": "build", 52 | "targets": [ 53 | "commonjs", 54 | "module", 55 | "typescript" 56 | ] 57 | }, 58 | "publishConfig": { 59 | "access": "public" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/web-browser/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-browsers'; 2 | export * from './use-warm-browser'; 3 | -------------------------------------------------------------------------------- /packages/web-browser/src/use-browsers.ts: -------------------------------------------------------------------------------- 1 | import { getCustomTabsSupportingBrowsersAsync } from 'expo-web-browser'; 2 | import { useEffect, useState } from 'react'; 3 | import { Platform } from 'react-native'; 4 | 5 | /** 6 | * Get a list of valid browser packages on Android. 7 | * This hook will always return an array, even when no browsers are available. 8 | * 9 | * @see https://docs.expo.io/versions/latest/sdk/webbrowser/#webbrowsergetcustomtabssupportingbrowsers 10 | * @remarks This only works on Android. 11 | * @example const browsers = useBrowsers(); 12 | */ 13 | export function useBrowsers(): string[] { 14 | const [browsers, setBrowsers] = useState([]); 15 | 16 | useEffect(() => { 17 | if (Platform.OS !== 'android') { 18 | return; 19 | } 20 | 21 | getCustomTabsSupportingBrowsersAsync().then( 22 | result => setBrowsers(result.browserPackages), 23 | ); 24 | }, []); 25 | 26 | return browsers; 27 | } 28 | -------------------------------------------------------------------------------- /packages/web-browser/src/use-warm-browser.ts: -------------------------------------------------------------------------------- 1 | import { coolDownAsync, warmUpAsync } from 'expo-web-browser'; 2 | import { useEffect } from 'react'; 3 | import { Platform } from 'react-native'; 4 | 5 | /** 6 | * Warm the provided browser. A list of browsers can be found by invoking `WebBrowser.getCustomTabsSupportingBrowsersAsync()`. 7 | * 8 | * @see https://docs.expo.io/versions/latest/sdk/webbrowser/#webbrowserwarmupasyncbrowserpackage 9 | * @remarks This only works on Android. 10 | * @example useWarmBrowser('com.android.chrome'); 11 | */ 12 | export function useWarmBrowser(browser?: string): void { 13 | useEffect(() => { 14 | if (Platform.OS !== 'android') { 15 | return; 16 | } 17 | 18 | if (browser) { 19 | warmUpAsync(browser); 20 | } 21 | 22 | return () => { 23 | coolDownAsync(browser); 24 | }; 25 | }, [browser]); 26 | } 27 | -------------------------------------------------------------------------------- /packages/web-browser/tests/use-browsers.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks'; 2 | import * as WebBrowser from 'expo-web-browser'; 3 | import { Platform } from 'react-native'; 4 | import { useBrowsers } from '../src/use-browsers'; 5 | 6 | const originalPlatform = Platform.OS; 7 | 8 | afterEach(() => { 9 | Platform.OS = originalPlatform; 10 | }); 11 | 12 | it('returns empty list for ios', async () => { 13 | const getter = jest.spyOn(WebBrowser, 'getCustomTabsSupportingBrowsersAsync'); 14 | 15 | Platform.OS = 'ios'; 16 | 17 | renderHook(useBrowsers); 18 | 19 | expect(getter).not.toBeCalled(); 20 | }); 21 | 22 | it('loads browser list for android when mounted', async () => { 23 | const browserPackages = ['com.android.chrome']; 24 | 25 | jest.spyOn(WebBrowser, 'getCustomTabsSupportingBrowsersAsync') 26 | .mockResolvedValue({ 27 | browserPackages, 28 | defaultBrowserPackage: 'com.android.chrome', 29 | preferredBrowserPackage: 'com.android.chrome', 30 | servicePackages: ['com.android.chrome'], 31 | }); 32 | 33 | Platform.OS = 'android'; 34 | 35 | const hook = renderHook(useBrowsers); 36 | await hook.waitForNextUpdate(); 37 | 38 | expect(hook.result.current).toBe(browserPackages); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/web-browser/tests/use-warm-browser.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks'; 2 | import * as WebBrowser from 'expo-web-browser'; 3 | import { Platform } from 'react-native'; 4 | import { useWarmBrowser } from '../src/use-warm-browser'; 5 | 6 | const originalPlatform = Platform.OS; 7 | 8 | afterEach(() => { 9 | Platform.OS = originalPlatform; 10 | }); 11 | 12 | it('does not warm or cool browser for ios', async () => { 13 | const warmer = jest.spyOn(WebBrowser, 'warmUpAsync'); 14 | const cooler = jest.spyOn(WebBrowser, 'coolDownAsync'); 15 | 16 | Platform.OS = 'ios'; 17 | 18 | renderHook(useWarmBrowser, { 19 | initialProps: 'com.android.chrome', 20 | }); 21 | 22 | expect(warmer).not.toBeCalled(); 23 | expect(cooler).not.toBeCalled(); 24 | }); 25 | 26 | it('warms and cools the browser for android when (un)mounted', async () => { 27 | const warmer = jest 28 | .spyOn(WebBrowser, 'warmUpAsync') 29 | .mockResolvedValue({ servicePackage: 'com.android.chrome' }); 30 | 31 | const cooler = jest 32 | .spyOn(WebBrowser, 'coolDownAsync') 33 | .mockResolvedValue({ servicePackage: 'com.android.chrome' }); 34 | 35 | Platform.OS = 'android'; 36 | 37 | const hook = renderHook(useWarmBrowser, { 38 | initialProps: 'com.android.chrome', 39 | }); 40 | 41 | expect(warmer).toBeCalledWith('com.android.chrome'); 42 | hook.unmount(); 43 | expect(cooler).toBeCalledWith('com.android.chrome'); 44 | }); 45 | 46 | it('warms and cools the browser for android when rerendered', async () => { 47 | const warmer = jest 48 | .spyOn(WebBrowser, 'warmUpAsync') 49 | .mockResolvedValue({ servicePackage: 'com.android.chrome' }); 50 | 51 | jest.spyOn(WebBrowser, 'coolDownAsync') 52 | .mockResolvedValue({ servicePackage: 'com.android.chrome' }); 53 | 54 | Platform.OS = 'android'; 55 | 56 | const hook = renderHook(useWarmBrowser, { 57 | initialProps: 'com.android.chrome', 58 | }); 59 | 60 | hook.rerender('com.microsoft.ie8'); 61 | expect(warmer).toBeCalledWith('com.microsoft.ie8'); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/web-browser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typescript" 5 | }, 6 | "include": [ 7 | "src" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "allowUnreachableCode": false, 5 | "allowUnusedLabels": false, 6 | "baseUrl": ".", 7 | "composite": true, 8 | "esModuleInterop": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "jsx": "react", 11 | "lib": [ 12 | "esnext", 13 | "dom" 14 | ], 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "noFallthroughCasesInSwitch": true, 18 | "noImplicitAny": true, 19 | "noStrictGenericChecks": false, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "paths": { 23 | "@use-expo/*": [ 24 | "./packages/*/src" 25 | ], 26 | }, 27 | "removeComments": false, 28 | "resolveJsonModule": true, 29 | "skipLibCheck": true, 30 | "sourceMap": true, 31 | "strict": true, 32 | "target": "esnext" 33 | }, 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------