├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .flowconfig
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── asana-attachment.yml
│ ├── asana-comment.yml
│ ├── pr-checks.yml
│ └── pr-rebase.yml
├── .gitignore
├── .husky
└── pre-commit
├── .prettierrc.json
├── .yarnrc
├── AirshipDemo
├── .eslintrc.json
├── .gitignore
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── airshipdemo
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── babel.config.js
├── index.ts
├── ios
│ ├── .xcode.env
│ ├── AirshipDemo.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── AirshipDemo.xcscheme
│ ├── AirshipDemo.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── AirshipDemo
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.mm
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ └── main.m
│ ├── Podfile
│ └── Podfile.lock
├── metro.config.js
├── package.json
├── patches
│ └── @react-native-community+blur+3.6.0.patch
├── react-native.config.js
├── src
│ ├── CustomFloatingComponent.tsx
│ ├── Demo.tsx
│ ├── TextInputModal.tsx
│ ├── ThemedDropdown.tsx
│ ├── ThemedModal.tsx
│ ├── ThemedToast.tsx
│ └── theming
│ │ ├── ThemeProvider.tsx
│ │ ├── ThemedButton.tsx
│ │ ├── ThemedText.tsx
│ │ ├── ThemedTextInput.tsx
│ │ └── themes.ts
├── tsconfig.json
└── yarn.lock
├── CHANGELOG.md
├── README.md
├── docs
├── custom-components.md
├── dropdown.md
├── dropdown.png
├── isometric.png
├── isometric.svg
├── modal.md
├── modal.png
├── toast.md
└── toast.png
├── package.json
├── src
├── components
│ ├── Airship.tsx
│ ├── AirshipDropdown.tsx
│ ├── AirshipModal.tsx
│ ├── AirshipToast.tsx
│ └── Barometer.tsx
├── index.flow.js
├── index.ts
├── types.ts
└── util
│ └── sides.ts
├── tsconfig.json
├── update-demo.sh
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /lib/
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "standard-kit/prettier",
4 | "standard-kit/prettier/jsx",
5 | "standard-kit/prettier/flow",
6 | "standard-kit/prettier/typescript",
7 | "standard-kit/prettier/react"
8 | ],
9 | "parserOptions": {
10 | "project": "tsconfig.json"
11 | },
12 | "plugins": [
13 | "simple-import-sort"
14 | ],
15 | "rules": {
16 | "simple-import-sort/imports": "error"
17 | },
18 | "settings": {
19 | "react": {
20 | "version": "16.9"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/AirshipDemo/.*
3 |
4 | [include]
5 |
6 | [libs]
7 |
8 | [lints]
9 |
10 | [options]
11 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### CHANGELOG
2 |
3 | Does this branch warrant an entry to the CHANGELOG?
4 |
5 | - [ ] Yes
6 | - [ ] No
7 |
8 | ### Dependencies
9 |
10 | none
11 |
12 | ### Description
13 |
14 | none
15 |
--------------------------------------------------------------------------------
/.github/workflows/asana-attachment.yml:
--------------------------------------------------------------------------------
1 | # removed
--------------------------------------------------------------------------------
/.github/workflows/asana-comment.yml:
--------------------------------------------------------------------------------
1 | # removed
--------------------------------------------------------------------------------
/.github/workflows/pr-checks.yml:
--------------------------------------------------------------------------------
1 | name: PR Checks
2 | on: [pull_request]
3 | jobs:
4 | block-wip-pr:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2.0.0
8 | - name: Block WIP PR
9 | uses: samholmes/block-wip-pr-action@v1.2.0
10 |
--------------------------------------------------------------------------------
/.github/workflows/pr-rebase.yml:
--------------------------------------------------------------------------------
1 | name: PR Rebase
2 | on:
3 | issue_comment:
4 | types: [created]
5 | jobs:
6 | rebase:
7 | name: Rebase
8 | if: >-
9 | github.event.issue.pull_request != '' &&
10 | (
11 | contains(github.event.comment.body, '/autosquash') ||
12 | contains(github.event.comment.body, '/fixup') ||
13 | contains(github.event.comment.body, '/rebase')
14 | )
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout the latest code
18 | uses: actions/checkout@v3
19 | with:
20 | token: ${{ secrets.GITHUB_TOKEN }}
21 | fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
22 | - name: Automatic Rebase
23 | uses: EdgeApp/rebase@changelog-resolver
24 | with:
25 | autosquash: ${{ true }}
26 | changelogResolver: ${{ true }}
27 | env:
28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build output:
2 | lib/
3 |
4 | # Package managers:
5 | node_modules/
6 | npm-debug.log
7 | package-lock.json
8 | yarn-error.log
9 |
10 | # Editors:
11 | .DS_Store
12 | .idea/
13 | .vscode/
14 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run precommit
5 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "avoid",
3 | "semi": false,
4 | "singleQuote": true,
5 | "trailingComma": "none"
6 | }
7 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | --ignore-scripts true
2 |
--------------------------------------------------------------------------------
/AirshipDemo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "project": "AirshipDemo/tsconfig.json"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/AirshipDemo/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | ios/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 | yarn-error.log
42 |
43 | # fastlane
44 | #
45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
46 | # screenshots whenever they are needed.
47 | # For more information about the recommended setup visit:
48 | # https://docs.fastlane.tools/best-practices/source-control/
49 |
50 | **/fastlane/report.xml
51 | **/fastlane/Preview.html
52 | **/fastlane/screenshots
53 | **/fastlane/test_output
54 |
55 | # Bundle artifact
56 | *.jsbundle
57 |
58 | # Ruby / CocoaPods
59 | /ios/Pods/
60 | /vendor/bundle/
61 |
62 | # Temporary files created by Metro to check the health of the file watcher
63 | .metro-health-check*
64 |
65 | # testing
66 | /coverage
67 |
--------------------------------------------------------------------------------
/AirshipDemo/README.md:
--------------------------------------------------------------------------------
1 | # react-native-airship Demo App
2 |
3 | This app shows off the react-native-airship built-in components.
4 |
5 | To launch the demo, first run `yarn install` in this folder to set up the dependencies, and then run either `yarn android` or `yarn ios`.
6 |
7 | If you want to edit the Airship library while using this demo, run the `update-demo.sh` script located in the outer folder to copy your changes into `node_modules`.
8 |
9 | ## Theming
10 |
11 | Most applications have unique color schemes. If you want to customize the Airship components to match your color scheme, it is simplest to make some lightweight wrappers around the built-in components. These wrappers pass in the right default colors, so you can use the wrapped components througout your app without worrying about appearances.
12 |
13 | See [ThemedModal](./src/ThemedModal.tsx) for an example of what theming a component might look like.
14 |
15 | This demo includes both light & dark themes based on the Solarized color scheme, and it uses the [react-native-patina](https://www.npmjs.com/package/react-native-patina) library to switch between them. This is just a demo, so feel free to use whatever theming approach you like.
16 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "org.jetbrains.kotlin.android"
3 | apply plugin: "com.facebook.react"
4 |
5 | /**
6 | * This is the configuration block to customize your React Native Android app.
7 | * By default you don't need to apply any configuration, just uncomment the lines you need.
8 | */
9 | react {
10 | /* Folders */
11 | // The root of your project, i.e. where "package.json" lives. Default is '..'
12 | // root = file("../")
13 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native
14 | // reactNativeDir = file("../node_modules/react-native")
15 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
16 | // codegenDir = file("../node_modules/@react-native/codegen")
17 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
18 | // cliFile = file("../node_modules/react-native/cli.js")
19 |
20 | /* Variants */
21 | // The list of variants to that are debuggable. For those we're going to
22 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
23 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
24 | // debuggableVariants = ["liteDebug", "prodDebug"]
25 |
26 | /* Bundling */
27 | // A list containing the node command and its flags. Default is just 'node'.
28 | // nodeExecutableAndArgs = ["node"]
29 | //
30 | // The command to run when bundling. By default is 'bundle'
31 | // bundleCommand = "ram-bundle"
32 | //
33 | // The path to the CLI configuration file. Default is empty.
34 | // bundleConfig = file(../rn-cli.config.js)
35 | //
36 | // The name of the generated asset file containing your JS bundle
37 | // bundleAssetName = "MyApplication.android.bundle"
38 | //
39 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
40 | // entryFile = file("../js/MyApplication.android.js")
41 | //
42 | // A list of extra flags to pass to the 'bundle' commands.
43 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
44 | // extraPackagerArgs = []
45 |
46 | /* Hermes Commands */
47 | // The hermes compiler command to run. By default it is 'hermesc'
48 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
49 | //
50 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
51 | // hermesFlags = ["-O", "-output-source-map"]
52 | }
53 |
54 | /**
55 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
56 | */
57 | def enableProguardInReleaseBuilds = false
58 |
59 | /**
60 | * The preferred build flavor of JavaScriptCore (JSC)
61 | *
62 | * For example, to use the international variant, you can use:
63 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
64 | *
65 | * The international variant includes ICU i18n library and necessary data
66 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
67 | * give correct results when using with locales other than en-US. Note that
68 | * this variant is about 6MiB larger per architecture than default.
69 | */
70 | def jscFlavor = 'org.webkit:android-jsc:+'
71 |
72 | android {
73 | ndkVersion rootProject.ext.ndkVersion
74 | buildToolsVersion rootProject.ext.buildToolsVersion
75 | compileSdk rootProject.ext.compileSdkVersion
76 |
77 | namespace "com.airshipdemo"
78 | defaultConfig {
79 | applicationId "com.airshipdemo"
80 | minSdkVersion rootProject.ext.minSdkVersion
81 | targetSdkVersion rootProject.ext.targetSdkVersion
82 | versionCode 1
83 | versionName "1.0"
84 | }
85 | signingConfigs {
86 | debug {
87 | storeFile file('debug.keystore')
88 | storePassword 'android'
89 | keyAlias 'androiddebugkey'
90 | keyPassword 'android'
91 | }
92 | }
93 | buildTypes {
94 | debug {
95 | signingConfig signingConfigs.debug
96 | }
97 | release {
98 | // Caution! In production, you need to generate your own keystore file.
99 | // see https://reactnative.dev/docs/signed-apk-android.
100 | signingConfig signingConfigs.debug
101 | minifyEnabled enableProguardInReleaseBuilds
102 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
103 | }
104 | }
105 | }
106 |
107 | dependencies {
108 | // The version of react-native is set by the React Native Gradle Plugin
109 | implementation("com.facebook.react:react-android")
110 | implementation("com.facebook.react:flipper-integration")
111 |
112 | if (hermesEnabled.toBoolean()) {
113 | implementation("com.facebook.react:hermes-android")
114 | } else {
115 | implementation jscFlavor
116 | }
117 | }
118 |
119 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
120 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/debug.keystore
--------------------------------------------------------------------------------
/AirshipDemo/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/java/com/airshipdemo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.airshipdemo
2 |
3 | import com.facebook.react.ReactActivity
4 | import com.facebook.react.ReactActivityDelegate
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate
7 |
8 | class MainActivity : ReactActivity() {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | override fun getMainComponentName(): String = "AirshipDemo"
15 |
16 | /**
17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
19 | */
20 | override fun createReactActivityDelegate(): ReactActivityDelegate =
21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
22 | }
23 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/java/com/airshipdemo/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.airshipdemo
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeHost
8 | import com.facebook.react.ReactPackage
9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 | import com.facebook.react.flipper.ReactNativeFlipper
13 | import com.facebook.soloader.SoLoader
14 |
15 | class MainApplication : Application(), ReactApplication {
16 |
17 | override val reactNativeHost: ReactNativeHost =
18 | object : DefaultReactNativeHost(this) {
19 | override fun getPackages(): List {
20 | // Packages that cannot be autolinked yet can be added manually here, for example:
21 | // packages.add(new MyReactNativePackage());
22 | return PackageList(this).packages
23 | }
24 |
25 | override fun getJSMainModuleName(): String = "index"
26 |
27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
28 |
29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
31 | }
32 |
33 | override val reactHost: ReactHost
34 | get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
35 |
36 | override fun onCreate() {
37 | super.onCreate()
38 | SoLoader.init(this, false)
39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
40 | // If you opted-in for the New Architecture, we load the native entry point for this app.
41 | load()
42 | }
43 | ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AirshipDemo
3 |
4 |
--------------------------------------------------------------------------------
/AirshipDemo/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AirshipDemo/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "34.0.0"
4 | minSdkVersion = 21
5 | compileSdkVersion = 34
6 | targetSdkVersion = 34
7 | ndkVersion = "25.1.8937393"
8 | kotlinVersion = "1.8.0"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/AirshipDemo/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Use this property to specify which architecture you want to build.
28 | # You can also override it from the CLI using
29 | # ./gradlew -PreactNativeArchitectures=x86_64
30 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
31 |
32 | # Use this property to enable support to the new architecture.
33 | # This will allow you to use TurboModules and the Fabric render in
34 | # your application. You should enable this flag either if you want
35 | # to write custom TurboModules/Fabric components OR use libraries that
36 | # are providing them.
37 | newArchEnabled=false
38 |
39 | # Use this property to enable or disable the Hermes JS engine.
40 | # If set to false, you will be using JSC instead.
41 | hermesEnabled=true
42 |
--------------------------------------------------------------------------------
/AirshipDemo/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/AirshipDemo/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/AirshipDemo/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/AirshipDemo/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command;
206 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
207 | # shell script including quotes and variable substitutions, so put them in
208 | # double quotes to make sure that they get re-expanded; and
209 | # * put everything else in single quotes, so that it's not re-expanded.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/AirshipDemo/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/AirshipDemo/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'AirshipDemo'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 | includeBuild('../node_modules/@react-native/gradle-plugin')
5 |
--------------------------------------------------------------------------------
/AirshipDemo/babel.config.js:
--------------------------------------------------------------------------------
1 | /* global module */
2 |
3 | module.exports = {
4 | presets: ['module:@react-native/babel-preset']
5 | }
6 |
--------------------------------------------------------------------------------
/AirshipDemo/index.ts:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native'
2 |
3 | import { Demo } from './src/Demo'
4 |
5 | AppRegistry.registerComponent('AirshipDemo', () => Demo)
6 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
11 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
12 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
13 | 797F98F9351E154851F83E0E /* libPods-AirshipDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BEE524C9133254F775B77B96 /* libPods-AirshipDemo.a */; };
14 | 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXFileReference section */
18 | 13B07F961A680F5B00A75B9A /* AirshipDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AirshipDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
19 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AirshipDemo/AppDelegate.h; sourceTree = ""; };
20 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.mm; path = AirshipDemo/AppDelegate.mm; sourceTree = ""; };
21 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = AirshipDemo/Images.xcassets; sourceTree = ""; };
22 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = AirshipDemo/Info.plist; sourceTree = ""; };
23 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = AirshipDemo/main.m; sourceTree = ""; };
24 | 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AirshipDemo/LaunchScreen.storyboard; sourceTree = ""; };
25 | A401D522B94AEDB975826F50 /* Pods-AirshipDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirshipDemo.release.xcconfig"; path = "Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo.release.xcconfig"; sourceTree = ""; };
26 | BEE524C9133254F775B77B96 /* libPods-AirshipDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AirshipDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
27 | E9D3E32E0BAA71D2C53D04FF /* Pods-AirshipDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirshipDemo.debug.xcconfig"; path = "Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo.debug.xcconfig"; sourceTree = ""; };
28 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | 797F98F9351E154851F83E0E /* libPods-AirshipDemo.a in Frameworks */,
37 | );
38 | runOnlyForDeploymentPostprocessing = 0;
39 | };
40 | /* End PBXFrameworksBuildPhase section */
41 |
42 | /* Begin PBXGroup section */
43 | 13B07FAE1A68108700A75B9A /* AirshipDemo */ = {
44 | isa = PBXGroup;
45 | children = (
46 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
47 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */,
48 | 13B07FB51A68108700A75B9A /* Images.xcassets */,
49 | 13B07FB61A68108700A75B9A /* Info.plist */,
50 | 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
51 | 13B07FB71A68108700A75B9A /* main.m */,
52 | );
53 | name = AirshipDemo;
54 | sourceTree = "";
55 | };
56 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
57 | isa = PBXGroup;
58 | children = (
59 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
60 | BEE524C9133254F775B77B96 /* libPods-AirshipDemo.a */,
61 | );
62 | name = Frameworks;
63 | sourceTree = "";
64 | };
65 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
66 | isa = PBXGroup;
67 | children = (
68 | );
69 | name = Libraries;
70 | sourceTree = "";
71 | };
72 | 83CBB9F61A601CBA00E9B192 = {
73 | isa = PBXGroup;
74 | children = (
75 | 13B07FAE1A68108700A75B9A /* AirshipDemo */,
76 | 832341AE1AAA6A7D00B99B32 /* Libraries */,
77 | 83CBBA001A601CBA00E9B192 /* Products */,
78 | 2D16E6871FA4F8E400B85C8A /* Frameworks */,
79 | AB4C4B0A0480EDBACD13ACD5 /* Pods */,
80 | );
81 | indentWidth = 2;
82 | sourceTree = "";
83 | tabWidth = 2;
84 | usesTabs = 0;
85 | };
86 | 83CBBA001A601CBA00E9B192 /* Products */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 13B07F961A680F5B00A75B9A /* AirshipDemo.app */,
90 | );
91 | name = Products;
92 | sourceTree = "";
93 | };
94 | AB4C4B0A0480EDBACD13ACD5 /* Pods */ = {
95 | isa = PBXGroup;
96 | children = (
97 | E9D3E32E0BAA71D2C53D04FF /* Pods-AirshipDemo.debug.xcconfig */,
98 | A401D522B94AEDB975826F50 /* Pods-AirshipDemo.release.xcconfig */,
99 | );
100 | path = Pods;
101 | sourceTree = "";
102 | };
103 | /* End PBXGroup section */
104 |
105 | /* Begin PBXNativeTarget section */
106 | 13B07F861A680F5B00A75B9A /* AirshipDemo */ = {
107 | isa = PBXNativeTarget;
108 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AirshipDemo" */;
109 | buildPhases = (
110 | 691C29C2F11E0531BC523123 /* [CP] Check Pods Manifest.lock */,
111 | FD10A7F022414F080027D42C /* Start Packager */,
112 | 13B07F871A680F5B00A75B9A /* Sources */,
113 | 13B07F8C1A680F5B00A75B9A /* Frameworks */,
114 | 13B07F8E1A680F5B00A75B9A /* Resources */,
115 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
116 | 97F9260948765A3BBE1D67F7 /* [CP] Embed Pods Frameworks */,
117 | 9409072AFF91E5F94985E931 /* [CP] Copy Pods Resources */,
118 | );
119 | buildRules = (
120 | );
121 | dependencies = (
122 | );
123 | name = AirshipDemo;
124 | productName = AirshipDemo;
125 | productReference = 13B07F961A680F5B00A75B9A /* AirshipDemo.app */;
126 | productType = "com.apple.product-type.application";
127 | };
128 | /* End PBXNativeTarget section */
129 |
130 | /* Begin PBXProject section */
131 | 83CBB9F71A601CBA00E9B192 /* Project object */ = {
132 | isa = PBXProject;
133 | attributes = {
134 | LastUpgradeCheck = 1210;
135 | TargetAttributes = {
136 | 13B07F861A680F5B00A75B9A = {
137 | LastSwiftMigration = 1120;
138 | };
139 | };
140 | };
141 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AirshipDemo" */;
142 | compatibilityVersion = "Xcode 12.0";
143 | developmentRegion = en;
144 | hasScannedForEncodings = 0;
145 | knownRegions = (
146 | en,
147 | Base,
148 | );
149 | mainGroup = 83CBB9F61A601CBA00E9B192;
150 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
151 | projectDirPath = "";
152 | projectRoot = "";
153 | targets = (
154 | 13B07F861A680F5B00A75B9A /* AirshipDemo */,
155 | );
156 | };
157 | /* End PBXProject section */
158 |
159 | /* Begin PBXResourcesBuildPhase section */
160 | 13B07F8E1A680F5B00A75B9A /* Resources */ = {
161 | isa = PBXResourcesBuildPhase;
162 | buildActionMask = 2147483647;
163 | files = (
164 | 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
165 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXShellScriptBuildPhase section */
172 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
173 | isa = PBXShellScriptBuildPhase;
174 | buildActionMask = 2147483647;
175 | files = (
176 | );
177 | inputPaths = (
178 | );
179 | name = "Bundle React Native code and images";
180 | outputPaths = (
181 | );
182 | runOnlyForDeploymentPostprocessing = 0;
183 | shellPath = /bin/sh;
184 | shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
185 | };
186 | 691C29C2F11E0531BC523123 /* [CP] Check Pods Manifest.lock */ = {
187 | isa = PBXShellScriptBuildPhase;
188 | buildActionMask = 2147483647;
189 | files = (
190 | );
191 | inputFileListPaths = (
192 | );
193 | inputPaths = (
194 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
195 | "${PODS_ROOT}/Manifest.lock",
196 | );
197 | name = "[CP] Check Pods Manifest.lock";
198 | outputFileListPaths = (
199 | );
200 | outputPaths = (
201 | "$(DERIVED_FILE_DIR)/Pods-AirshipDemo-checkManifestLockResult.txt",
202 | );
203 | runOnlyForDeploymentPostprocessing = 0;
204 | shellPath = /bin/sh;
205 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
206 | showEnvVarsInLog = 0;
207 | };
208 | 9409072AFF91E5F94985E931 /* [CP] Copy Pods Resources */ = {
209 | isa = PBXShellScriptBuildPhase;
210 | buildActionMask = 2147483647;
211 | files = (
212 | );
213 | inputFileListPaths = (
214 | "${PODS_ROOT}/Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo-resources-${CONFIGURATION}-input-files.xcfilelist",
215 | );
216 | name = "[CP] Copy Pods Resources";
217 | outputFileListPaths = (
218 | "${PODS_ROOT}/Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo-resources-${CONFIGURATION}-output-files.xcfilelist",
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | shellPath = /bin/sh;
222 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo-resources.sh\"\n";
223 | showEnvVarsInLog = 0;
224 | };
225 | 97F9260948765A3BBE1D67F7 /* [CP] Embed Pods Frameworks */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | inputFileListPaths = (
231 | "${PODS_ROOT}/Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo-frameworks-${CONFIGURATION}-input-files.xcfilelist",
232 | );
233 | name = "[CP] Embed Pods Frameworks";
234 | outputFileListPaths = (
235 | "${PODS_ROOT}/Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo-frameworks-${CONFIGURATION}-output-files.xcfilelist",
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | shellPath = /bin/sh;
239 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AirshipDemo/Pods-AirshipDemo-frameworks.sh\"\n";
240 | showEnvVarsInLog = 0;
241 | };
242 | FD10A7F022414F080027D42C /* Start Packager */ = {
243 | isa = PBXShellScriptBuildPhase;
244 | buildActionMask = 2147483647;
245 | files = (
246 | );
247 | inputFileListPaths = (
248 | );
249 | inputPaths = (
250 | );
251 | name = "Start Packager";
252 | outputFileListPaths = (
253 | );
254 | outputPaths = (
255 | );
256 | runOnlyForDeploymentPostprocessing = 0;
257 | shellPath = /bin/sh;
258 | shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
259 | showEnvVarsInLog = 0;
260 | };
261 | /* End PBXShellScriptBuildPhase section */
262 |
263 | /* Begin PBXSourcesBuildPhase section */
264 | 13B07F871A680F5B00A75B9A /* Sources */ = {
265 | isa = PBXSourcesBuildPhase;
266 | buildActionMask = 2147483647;
267 | files = (
268 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
269 | 13B07FC11A68108700A75B9A /* main.m in Sources */,
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | };
273 | /* End PBXSourcesBuildPhase section */
274 |
275 | /* Begin XCBuildConfiguration section */
276 | 13B07F941A680F5B00A75B9A /* Debug */ = {
277 | isa = XCBuildConfiguration;
278 | baseConfigurationReference = E9D3E32E0BAA71D2C53D04FF /* Pods-AirshipDemo.debug.xcconfig */;
279 | buildSettings = {
280 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
281 | CLANG_ENABLE_MODULES = YES;
282 | CURRENT_PROJECT_VERSION = 1;
283 | ENABLE_BITCODE = NO;
284 | INFOPLIST_FILE = AirshipDemo/Info.plist;
285 | LD_RUNPATH_SEARCH_PATHS = (
286 | "$(inherited)",
287 | "@executable_path/Frameworks",
288 | );
289 | MARKETING_VERSION = 1.0;
290 | OTHER_LDFLAGS = (
291 | "$(inherited)",
292 | "-ObjC",
293 | "-lc++",
294 | );
295 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
296 | PRODUCT_NAME = AirshipDemo;
297 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
298 | SWIFT_VERSION = 5.0;
299 | VERSIONING_SYSTEM = "apple-generic";
300 | };
301 | name = Debug;
302 | };
303 | 13B07F951A680F5B00A75B9A /* Release */ = {
304 | isa = XCBuildConfiguration;
305 | baseConfigurationReference = A401D522B94AEDB975826F50 /* Pods-AirshipDemo.release.xcconfig */;
306 | buildSettings = {
307 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
308 | CLANG_ENABLE_MODULES = YES;
309 | CURRENT_PROJECT_VERSION = 1;
310 | INFOPLIST_FILE = AirshipDemo/Info.plist;
311 | LD_RUNPATH_SEARCH_PATHS = (
312 | "$(inherited)",
313 | "@executable_path/Frameworks",
314 | );
315 | MARKETING_VERSION = 1.0;
316 | OTHER_LDFLAGS = (
317 | "$(inherited)",
318 | "-ObjC",
319 | "-lc++",
320 | );
321 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
322 | PRODUCT_NAME = AirshipDemo;
323 | SWIFT_VERSION = 5.0;
324 | VERSIONING_SYSTEM = "apple-generic";
325 | };
326 | name = Release;
327 | };
328 | 83CBBA201A601CBA00E9B192 /* Debug */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ALWAYS_SEARCH_USER_PATHS = NO;
332 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
333 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
338 | CLANG_WARN_BOOL_CONVERSION = YES;
339 | CLANG_WARN_COMMA = YES;
340 | CLANG_WARN_CONSTANT_CONVERSION = YES;
341 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
342 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
343 | CLANG_WARN_EMPTY_BODY = YES;
344 | CLANG_WARN_ENUM_CONVERSION = YES;
345 | CLANG_WARN_INFINITE_RECURSION = YES;
346 | CLANG_WARN_INT_CONVERSION = YES;
347 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
348 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
349 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
351 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
352 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
353 | CLANG_WARN_STRICT_PROTOTYPES = YES;
354 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
355 | CLANG_WARN_UNREACHABLE_CODE = YES;
356 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
357 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
358 | COPY_PHASE_STRIP = NO;
359 | ENABLE_STRICT_OBJC_MSGSEND = YES;
360 | ENABLE_TESTABILITY = YES;
361 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
362 | GCC_C_LANGUAGE_STANDARD = gnu99;
363 | GCC_DYNAMIC_NO_PIC = NO;
364 | GCC_NO_COMMON_BLOCKS = YES;
365 | GCC_OPTIMIZATION_LEVEL = 0;
366 | GCC_PREPROCESSOR_DEFINITIONS = (
367 | "DEBUG=1",
368 | "$(inherited)",
369 | );
370 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
373 | GCC_WARN_UNDECLARED_SELECTOR = YES;
374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
375 | GCC_WARN_UNUSED_FUNCTION = YES;
376 | GCC_WARN_UNUSED_VARIABLE = YES;
377 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
378 | LD_RUNPATH_SEARCH_PATHS = (
379 | /usr/lib/swift,
380 | "$(inherited)",
381 | );
382 | LIBRARY_SEARCH_PATHS = (
383 | "\"$(SDKROOT)/usr/lib/swift\"",
384 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
385 | "\"$(inherited)\"",
386 | );
387 | MTL_ENABLE_DEBUG_INFO = YES;
388 | ONLY_ACTIVE_ARCH = YES;
389 | OTHER_CFLAGS = "$(inherited)";
390 | OTHER_CPLUSPLUSFLAGS = (
391 | "$(OTHER_CFLAGS)",
392 | "-DFOLLY_NO_CONFIG",
393 | "-DFOLLY_MOBILE=1",
394 | "-DFOLLY_USE_LIBCPP=1",
395 | "-DFOLLY_CFG_NO_COROUTINES=1",
396 | );
397 | OTHER_LDFLAGS = (
398 | "$(inherited)",
399 | "-Wl",
400 | "-ld_classic",
401 | );
402 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
403 | SDKROOT = iphoneos;
404 | USE_HERMES = true;
405 | };
406 | name = Debug;
407 | };
408 | 83CBBA211A601CBA00E9B192 /* Release */ = {
409 | isa = XCBuildConfiguration;
410 | buildSettings = {
411 | ALWAYS_SEARCH_USER_PATHS = NO;
412 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
413 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
414 | CLANG_CXX_LIBRARY = "libc++";
415 | CLANG_ENABLE_MODULES = YES;
416 | CLANG_ENABLE_OBJC_ARC = YES;
417 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
418 | CLANG_WARN_BOOL_CONVERSION = YES;
419 | CLANG_WARN_COMMA = YES;
420 | CLANG_WARN_CONSTANT_CONVERSION = YES;
421 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
422 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
423 | CLANG_WARN_EMPTY_BODY = YES;
424 | CLANG_WARN_ENUM_CONVERSION = YES;
425 | CLANG_WARN_INFINITE_RECURSION = YES;
426 | CLANG_WARN_INT_CONVERSION = YES;
427 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
428 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
429 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
430 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
431 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
432 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
433 | CLANG_WARN_STRICT_PROTOTYPES = YES;
434 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
435 | CLANG_WARN_UNREACHABLE_CODE = YES;
436 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
437 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
438 | COPY_PHASE_STRIP = YES;
439 | ENABLE_NS_ASSERTIONS = NO;
440 | ENABLE_STRICT_OBJC_MSGSEND = YES;
441 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
442 | GCC_C_LANGUAGE_STANDARD = gnu99;
443 | GCC_NO_COMMON_BLOCKS = YES;
444 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
445 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
446 | GCC_WARN_UNDECLARED_SELECTOR = YES;
447 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
448 | GCC_WARN_UNUSED_FUNCTION = YES;
449 | GCC_WARN_UNUSED_VARIABLE = YES;
450 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
451 | LD_RUNPATH_SEARCH_PATHS = (
452 | /usr/lib/swift,
453 | "$(inherited)",
454 | );
455 | LIBRARY_SEARCH_PATHS = (
456 | "\"$(SDKROOT)/usr/lib/swift\"",
457 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
458 | "\"$(inherited)\"",
459 | );
460 | MTL_ENABLE_DEBUG_INFO = NO;
461 | OTHER_CFLAGS = "$(inherited)";
462 | OTHER_CPLUSPLUSFLAGS = (
463 | "$(OTHER_CFLAGS)",
464 | "-DFOLLY_NO_CONFIG",
465 | "-DFOLLY_MOBILE=1",
466 | "-DFOLLY_USE_LIBCPP=1",
467 | "-DFOLLY_CFG_NO_COROUTINES=1",
468 | );
469 | OTHER_LDFLAGS = (
470 | "$(inherited)",
471 | "-Wl",
472 | "-ld_classic",
473 | );
474 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
475 | SDKROOT = iphoneos;
476 | USE_HERMES = true;
477 | VALIDATE_PRODUCT = YES;
478 | };
479 | name = Release;
480 | };
481 | /* End XCBuildConfiguration section */
482 |
483 | /* Begin XCConfigurationList section */
484 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AirshipDemo" */ = {
485 | isa = XCConfigurationList;
486 | buildConfigurations = (
487 | 13B07F941A680F5B00A75B9A /* Debug */,
488 | 13B07F951A680F5B00A75B9A /* Release */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AirshipDemo" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | 83CBBA201A601CBA00E9B192 /* Debug */,
497 | 83CBBA211A601CBA00E9B192 /* Release */,
498 | );
499 | defaultConfigurationIsVisible = 0;
500 | defaultConfigurationName = Release;
501 | };
502 | /* End XCConfigurationList section */
503 | };
504 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
505 | }
506 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo.xcodeproj/xcshareddata/xcschemes/AirshipDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : RCTAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 |
5 | @implementation AppDelegate
6 |
7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
8 | {
9 | self.moduleName = @"AirshipDemo";
10 | // You can add your custom initial props in the dictionary below.
11 | // They will be passed down to the ViewController used by React Native.
12 | self.initialProps = @{};
13 |
14 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
15 | }
16 |
17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
18 | {
19 | return [self getBundleURL];
20 | }
21 |
22 | - (NSURL *)getBundleURL
23 | {
24 | #if DEBUG
25 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
26 | #else
27 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
28 | #endif
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "scale" : "1x",
46 | "size" : "1024x1024"
47 | }
48 | ],
49 | "info" : {
50 | "author" : "xcode",
51 | "version" : 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | AirshipDemo
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSAllowsLocalNetworking
32 |
33 |
34 | NSLocationWhenInUseUsageDescription
35 |
36 | UILaunchStoryboardName
37 | LaunchScreen
38 | UIRequiredDeviceCapabilities
39 |
40 | armv7
41 |
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UIViewControllerBasedStatusBarAppearance
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/AirshipDemo/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/AirshipDemo/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
12 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
13 | #
14 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
15 | # ```js
16 | # module.exports = {
17 | # dependencies: {
18 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
19 | # ```
20 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
21 |
22 | linkage = ENV['USE_FRAMEWORKS']
23 | if linkage != nil
24 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
25 | use_frameworks! :linkage => linkage.to_sym
26 | end
27 |
28 | target 'AirshipDemo' do
29 | config = use_native_modules!
30 |
31 | use_react_native!(
32 | :path => config[:reactNativePath],
33 | # Enables Flipper.
34 | #
35 | # Note that if you have use_frameworks! enabled, Flipper will not work and
36 | # you should disable the next line.
37 | :flipper_configuration => flipper_config,
38 | # An absolute path to your application root.
39 | :app_path => "#{Pod::Config.instance.installation_root}/.."
40 | )
41 |
42 | post_install do |installer|
43 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
44 | react_native_post_install(
45 | installer,
46 | config[:reactNativePath],
47 | :mac_catalyst_enabled => false
48 | )
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/AirshipDemo/metro.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname module require */
2 |
3 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
4 |
5 | /**
6 | * Metro configuration
7 | * https://facebook.github.io/metro/docs/configuration
8 | *
9 | * @type {import('metro-config').MetroConfig}
10 | */
11 | const config = {}
12 |
13 | module.exports = mergeConfig(getDefaultConfig(__dirname), config)
14 |
--------------------------------------------------------------------------------
/AirshipDemo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "airship-demo",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "prepare": "patch-package",
9 | "prepare-ios": "(cd ios; pod install --repo-update)",
10 | "start": "react-native start"
11 | },
12 | "dependencies": {
13 | "@react-native-community/blur": "^3.6.0",
14 | "react": "18.2.0",
15 | "react-native": "0.73.0",
16 | "react-native-airship": "*",
17 | "react-native-patina": "^0.1.3"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.20.0",
21 | "@babel/preset-env": "^7.20.0",
22 | "@babel/runtime": "^7.20.0",
23 | "@react-native/babel-preset": "^0.73.18",
24 | "@react-native/metro-config": "^0.73.2",
25 | "@react-native/typescript-config": "^0.73.1",
26 | "@types/react": "^18.2.6",
27 | "@types/react-test-renderer": "^18.0.0",
28 | "eslint": "^8.19.0",
29 | "patch-package": "^8.0.0",
30 | "prettier": "2.8.8",
31 | "typescript": "^4.9.5"
32 | },
33 | "engines": {
34 | "node": ">=18"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/AirshipDemo/patches/@react-native-community+blur+3.6.0.patch:
--------------------------------------------------------------------------------
1 | diff --git a/node_modules/@react-native-community/blur/android/build.gradle b/node_modules/@react-native-community/blur/android/build.gradle
2 | index 8177235..fcdeb1c 100644
3 | --- a/node_modules/@react-native-community/blur/android/build.gradle
4 | +++ b/node_modules/@react-native-community/blur/android/build.gradle
5 | @@ -9,7 +9,6 @@ buildscript {
6 | if (project == rootProject) {
7 | repositories {
8 | google()
9 | - jcenter()
10 | }
11 |
12 | dependencies {
13 | @@ -37,11 +36,12 @@ android {
14 |
15 | repositories {
16 | google()
17 | - jcenter()
18 | + maven { url 'https://jitpack.io' }
19 | }
20 |
21 | dependencies {
22 | //noinspection GradleDynamicVersion
23 | implementation 'com.facebook.react:react-native:+'
24 | - implementation 'com.eightbitlab:blurview:1.6.3'
25 | + implementation 'com.github.Dimezis:BlurView:version-1.6.6'
26 | + implementation 'com.github.android:renderscript-intrinsics-replacement-toolkit:b6363490c3'
27 | }
28 | diff --git a/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/BlurViewManager.java b/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/BlurViewManager.java
29 | index b583b02..fa319d1 100644
30 | --- a/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/BlurViewManager.java
31 | +++ b/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/BlurViewManager.java
32 | @@ -1,5 +1,6 @@
33 | package com.cmcewen.blurview;
34 |
35 | +import android.app.Activity;
36 | import android.graphics.drawable.Drawable;
37 | import android.view.View;
38 | import android.view.ViewGroup;
39 | @@ -14,7 +15,6 @@ import java.util.Objects;
40 | import javax.annotation.Nonnull;
41 |
42 | import eightbitlab.com.blurview.BlurView;
43 | -import eightbitlab.com.blurview.RenderScriptBlur;
44 |
45 |
46 | @SuppressWarnings("unused")
47 | @@ -32,14 +32,17 @@ class BlurViewManager extends ViewGroupManager {
48 | @Override
49 | public @Nonnull BlurView createViewInstance(@Nonnull ThemedReactContext ctx) {
50 | BlurView blurView = new BlurView(ctx);
51 | - View decorView = Objects.requireNonNull(ctx.getCurrentActivity()).getWindow().getDecorView();
52 | - ViewGroup rootView = decorView.findViewById(android.R.id.content);
53 | - Drawable windowBackground = decorView.getBackground();
54 | - blurView.setupWith(rootView)
55 | - .setFrameClearDrawable(windowBackground)
56 | - .setBlurAlgorithm(new RenderScriptBlur(ctx))
57 | - .setBlurRadius(defaultRadius)
58 | - .setHasFixedTransformationMatrix(false);
59 | + Activity currentActivity = ctx.getCurrentActivity();
60 | + if (currentActivity != null) {
61 | + View decorView = currentActivity.getWindow().getDecorView();
62 | + ViewGroup rootView = decorView.findViewById(android.R.id.content);
63 | + Drawable windowBackground = decorView.getBackground();
64 | + blurView.setupWith(rootView)
65 | + .setFrameClearDrawable(windowBackground)
66 | + .setBlurAlgorithm(new SimdBlur())
67 | + .setBlurRadius(defaultRadius)
68 | + .setHasFixedTransformationMatrix(false);
69 | + }
70 | return blurView;
71 | }
72 |
73 | diff --git a/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/SimdBlur.java b/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/SimdBlur.java
74 | new file mode 100644
75 | index 0000000..1d15b8d
76 | --- /dev/null
77 | +++ b/node_modules/@react-native-community/blur/android/src/main/java/com/cmcewen/blurview/SimdBlur.java
78 | @@ -0,0 +1,32 @@
79 | +package com.cmcewen.blurview;
80 | +
81 | +import android.graphics.Bitmap;
82 | +import androidx.annotation.NonNull;
83 | +import com.google.android.renderscript.Toolkit;
84 | +import eightbitlab.com.blurview.BlurAlgorithm;
85 | +
86 | +public final class SimdBlur implements BlurAlgorithm {
87 | + /**
88 | + * @param bitmap bitmap to blur
89 | + * @param blurRadius blur radius (1..25)
90 | + * @return blurred bitmap
91 | + */
92 | + @Override
93 | + public final Bitmap blur(Bitmap bitmap, float blurRadius) {
94 | + return Toolkit.INSTANCE.blur(bitmap, (int) blurRadius);
95 | + }
96 | +
97 | + @Override
98 | + public boolean canModifyBitmap() {
99 | + return true;
100 | + }
101 | +
102 | + @Override
103 | + public final void destroy() {}
104 | +
105 | + @NonNull
106 | + @Override
107 | + public Bitmap.Config getSupportedBitmapConfig() {
108 | + return Bitmap.Config.ARGB_8888;
109 | + }
110 | +}
111 | diff --git a/node_modules/@react-native-community/blur/src/BlurView.android.js b/node_modules/@react-native-community/blur/src/BlurView.android.js
112 | index a192476..3a9c5b3 100644
113 | --- a/node_modules/@react-native-community/blur/src/BlurView.android.js
114 | +++ b/node_modules/@react-native-community/blur/src/BlurView.android.js
115 | @@ -5,6 +5,7 @@ import {
116 | requireNativeComponent,
117 | DeviceEventEmitter,
118 | ViewPropTypes,
119 | + Platform,
120 | StyleSheet,
121 | } from 'react-native';
122 |
123 | @@ -65,6 +66,14 @@ class BlurView extends Component {
124 | render() {
125 | const { style } = this.props;
126 |
127 | + if (Platform.Version <= 27) {
128 | + return (
129 | +
130 | + {this.props.children}
131 | +
132 | + )
133 | + }
134 | +
135 | return (
136 |
7 | }
8 |
9 | /**
10 | * An example of how easy it is to build floating UI elements using Airship.
11 | */
12 | export function CustomFloatingComponent(props: Props): JSX.Element {
13 | const { bridge } = props
14 | React.useEffect(() => bridge.on('clear', bridge.resolve), [bridge])
15 | React.useEffect(() => bridge.on('result', bridge.remove), [bridge])
16 |
17 | return (
18 | bridge.resolve()}>
19 | Tap to dismiss
20 |
21 | )
22 | }
23 |
24 | const styles = StyleSheet.create({
25 | text: {
26 | backgroundColor: 'rgba(255, 255, 255, 0.75)',
27 | borderRadius: 10,
28 | color: 'black',
29 | fontSize: 12,
30 | margin: 10,
31 | padding: 10,
32 | textAlign: 'center'
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/AirshipDemo/src/Demo.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | Appearance,
4 | Platform,
5 | SafeAreaView,
6 | ScrollView,
7 | StatusBar,
8 | StyleSheet,
9 | Switch,
10 | View
11 | } from 'react-native'
12 | import { makeAirship } from 'react-native-airship'
13 | import { cacheStyles } from 'react-native-patina'
14 |
15 | import { CustomFloatingComponent } from './CustomFloatingComponent'
16 | import { TextInputModal } from './TextInputModal'
17 | import { ThemedDropdown } from './ThemedDropdown'
18 | import { ThemedToast } from './ThemedToast'
19 | import { ThemedButton } from './theming/ThemedButton'
20 | import { ThemedText } from './theming/ThemedText'
21 | import { changeTheme, Theme, ThemeProvider } from './theming/ThemeProvider'
22 | import { darkTheme, lightTheme } from './theming/themes'
23 |
24 | const Airship = makeAirship()
25 |
26 | /**
27 | * The demo app. This has a main scroll view with a handful of buttons
28 | * for launching different Airship components.
29 | */
30 | export const Demo = (props: {}): JSX.Element => {
31 | // Switch states:
32 | const [dark, setDark] = React.useState(Appearance.getColorScheme() === 'dark')
33 | const [translucent, setTranslucent] = React.useState(true)
34 |
35 | // Theming:
36 | const theme = dark ? darkTheme : lightTheme
37 | React.useEffect(() => changeTheme(theme), [theme])
38 | const styles = getStyles(theme)
39 | const marginTop =
40 | translucent && StatusBar.currentHeight != null ? StatusBar.currentHeight : 0
41 |
42 | return (
43 |
44 |
45 |
50 |
51 |
55 | Airship Demo
56 |
57 | Press some buttons to launch the demo components
58 |
59 | Modal
60 |
61 | Centered modal
62 |
63 | Dropdown
64 | Toast
65 | Custom component
66 | Clear all
67 |
68 | Dark mode
69 |
74 |
75 | {Platform.OS === 'android' ? (
76 |
77 | Translucent status bar
78 |
83 |
84 | ) : null}
85 |
86 |
87 |
88 |
89 | )
90 | }
91 |
92 | const getStyles = cacheStyles((theme: Theme) => ({
93 | screen: {
94 | ...StyleSheet.absoluteFillObject,
95 | backgroundColor: theme.background
96 | },
97 | scrollView: {
98 | flex: 1
99 | },
100 | scrollContents: {
101 | padding: theme.rem(1)
102 | },
103 | row: {
104 | alignItems: 'center',
105 | flexDirection: 'row',
106 | justifyContent: 'space-between'
107 | }
108 | }))
109 |
110 | // Callbacks for demo components:
111 |
112 | function handleModal(): void {
113 | Airship.show(bridge => {
114 | return
115 | }).catch(ignoreError)
116 | }
117 |
118 | function handleCenterModal(): void {
119 | Airship.show(bridge => {
120 | return
121 | }).catch(ignoreError)
122 | }
123 |
124 | function handleDropdown(): void {
125 | Airship.show(bridge => {
126 | return (
127 | Alert: This is a dropdown
128 | )
129 | }).catch(ignoreError)
130 | }
131 |
132 | function handleToast(): void {
133 | Airship.show(bridge => {
134 | return
135 | }).catch(ignoreError)
136 | }
137 |
138 | function handleCustom(): void {
139 | Airship.show(bridge => {
140 | return
141 | }).catch(ignoreError)
142 | }
143 |
144 | function handleClear(): void {
145 | Airship.clear()
146 | }
147 |
148 | const ignoreError = (): void => undefined
149 |
--------------------------------------------------------------------------------
/AirshipDemo/src/TextInputModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { AirshipBridge } from 'react-native-airship'
3 |
4 | import { ThemedModal } from './ThemedModal'
5 | import { ThemedButton } from './theming/ThemedButton'
6 | import { ThemedText } from './theming/ThemedText'
7 | import { ThemedTextInput } from './theming/ThemedTextInput'
8 |
9 | interface Props {
10 | bridge: AirshipBridge
11 | center?: boolean
12 | }
13 |
14 | /**
15 | * This is an example of how to use the AirshipModal component
16 | * as a building-block of a larger modal component.
17 | * This one allows the user to enter some text,
18 | * which it returns as a promise.
19 | */
20 | export function TextInputModal(props: Props): JSX.Element {
21 | const { bridge, center = false } = props
22 | const [text, setText] = React.useState('text input')
23 |
24 | return (
25 | bridge.resolve(undefined)}
29 | >
30 | A Modal
31 |
32 | The Airship modal slides in from the bottom of the screen. It can have
33 | any contents you like.
34 |
35 |
36 | bridge.resolve(text)}>
37 | Sounds good
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/AirshipDemo/src/ThemedDropdown.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Text } from 'react-native'
3 | import { AirshipDropdown, AirshipDropdownProps } from 'react-native-airship'
4 | import { cacheStyles } from 'react-native-patina'
5 |
6 | import { Theme, useTheme } from './theming/ThemeProvider'
7 |
8 | export function ThemedDropdown(props: AirshipDropdownProps): JSX.Element {
9 | const { children } = props
10 | const theme = useTheme()
11 | const styles = getStyles(theme)
12 |
13 | return (
14 |
20 | {children}
21 |
22 | )
23 | }
24 |
25 | const getStyles = cacheStyles((theme: Theme) => ({
26 | dropdownText: {
27 | alignSelf: 'center',
28 | color: theme.dropdownText,
29 | flexShrink: 1,
30 | fontSize: theme.rem(1),
31 | margin: theme.rem(1)
32 | }
33 | }))
34 |
--------------------------------------------------------------------------------
/AirshipDemo/src/ThemedModal.tsx:
--------------------------------------------------------------------------------
1 | import { BlurView } from '@react-native-community/blur'
2 | import * as React from 'react'
3 | import { StyleSheet } from 'react-native'
4 | import { AirshipModal, AirshipModalProps } from 'react-native-airship'
5 |
6 | import { useTheme } from './theming/ThemeProvider'
7 |
8 | export function ThemedModal(props: AirshipModalProps): JSX.Element {
9 | const { center = false } = props
10 | const theme = useTheme()
11 |
12 | return (
13 | }
19 | {...props}
20 | />
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/AirshipDemo/src/ThemedToast.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { AirshipToast, AirshipToastProps } from 'react-native-airship'
3 |
4 | import { useTheme } from './theming/ThemeProvider'
5 |
6 | export function ThemedToast(props: AirshipToastProps): JSX.Element {
7 | const theme = useTheme()
8 |
9 | return (
10 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/AirshipDemo/src/theming/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | import { Appearance } from 'react-native'
2 | import { makeThemeContext, setCacheSize } from 'react-native-patina'
3 |
4 | import { darkTheme, lightTheme } from './themes'
5 |
6 | // Make room for two themes:
7 | setCacheSize(2)
8 |
9 | export interface Theme {
10 | rem: (size: number) => number
11 | background: string
12 | text: string
13 | header: string
14 |
15 | button: string
16 | buttonText: string
17 |
18 | dropdown: string
19 | dropdownText: string
20 |
21 | textInput: string
22 | textInputBorder: string
23 | textInputText: string
24 |
25 | toast: string
26 | toastText: string
27 | }
28 |
29 | export interface ThemeProps {
30 | theme: Theme
31 | }
32 |
33 | export const { changeTheme, ThemeProvider, useTheme, withTheme } =
34 | makeThemeContext(
35 | Appearance.getColorScheme() === 'dark' ? darkTheme : lightTheme
36 | )
37 |
--------------------------------------------------------------------------------
/AirshipDemo/src/theming/ThemedButton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Text, TouchableOpacity } from 'react-native'
3 | import { cacheStyles } from 'react-native-patina'
4 |
5 | import { Theme, useTheme } from './ThemeProvider'
6 |
7 | interface Props {
8 | children: string | React.ReactNode
9 | onPress: () => void
10 | }
11 |
12 | export function ThemedButton(props: Props): JSX.Element {
13 | const theme = useTheme()
14 | const styles = getStyles(theme)
15 | const { children, onPress } = props
16 |
17 | return (
18 |
19 | {typeof children === 'string' ? (
20 | {children}
21 | ) : (
22 | children
23 | )}
24 |
25 | )
26 | }
27 |
28 | const getStyles = cacheStyles((theme: Theme) => ({
29 | button: {
30 | alignItems: 'center',
31 | backgroundColor: theme.button,
32 | borderRadius: theme.rem(1),
33 | justifyContent: 'center',
34 | margin: theme.rem(1),
35 | padding: theme.rem(1),
36 | flexDirection: 'row'
37 | },
38 | buttonText: {
39 | color: theme.buttonText,
40 | fontSize: theme.rem(1)
41 | }
42 | }))
43 |
--------------------------------------------------------------------------------
/AirshipDemo/src/theming/ThemedText.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Text } from 'react-native'
3 | import { cacheStyles } from 'react-native-patina'
4 |
5 | import { Theme, useTheme } from './ThemeProvider'
6 |
7 | interface Props {
8 | children: React.ReactNode
9 | header?: boolean
10 | }
11 |
12 | export function ThemedText(props: Props): JSX.Element {
13 | const { children, header = false } = props
14 | const theme = useTheme()
15 | const styles = getStyles(theme)
16 |
17 | return {children}
18 | }
19 |
20 | const getStyles = cacheStyles((theme: Theme) => ({
21 | header: {
22 | alignSelf: 'center',
23 | color: theme.header,
24 | fontSize: theme.rem(1.2),
25 | margin: theme.rem(1),
26 | marginBottom: 0
27 | },
28 | text: {
29 | color: theme.text,
30 | fontSize: theme.rem(1),
31 | margin: theme.rem(1)
32 | }
33 | }))
34 |
--------------------------------------------------------------------------------
/AirshipDemo/src/theming/ThemedTextInput.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Keyboard, TextInput, TouchableOpacity, View } from 'react-native'
3 | import { cacheStyles } from 'react-native-patina'
4 |
5 | import { ThemedText } from './ThemedText'
6 | import { Theme, useTheme } from './ThemeProvider'
7 |
8 | interface Props {
9 | value: string
10 | onChangeText: (text: string) => void
11 | }
12 |
13 | export function ThemedTextInput(props: Props): JSX.Element {
14 | const { value, onChangeText } = props
15 | const theme = useTheme()
16 | const styles = getStyles(theme)
17 |
18 | return (
19 |
20 |
25 | Keyboard.dismiss()}>
26 | Hide keyboard
27 |
28 |
29 | )
30 | }
31 |
32 | const getStyles = cacheStyles((theme: Theme) => ({
33 | input: {
34 | backgroundColor: theme.textInput,
35 | borderColor: theme.textInputBorder,
36 | borderRadius: theme.rem(0.5),
37 | borderWidth: theme.rem(0.1),
38 | color: theme.textInputText,
39 | flex: 1,
40 | fontSize: theme.rem(1),
41 | marginBottom: theme.rem(1),
42 | marginLeft: theme.rem(1),
43 | marginTop: theme.rem(1),
44 | padding: theme.rem(0.5)
45 | },
46 | row: {
47 | alignItems: 'center',
48 | flexDirection: 'row',
49 | justifyContent: 'space-between'
50 | }
51 | }))
52 |
--------------------------------------------------------------------------------
/AirshipDemo/src/theming/themes.ts:
--------------------------------------------------------------------------------
1 | import { Theme } from './ThemeProvider'
2 |
3 | const solarized = {
4 | base03: '#002b36',
5 | base02: '#073642',
6 | base01: '#586e75',
7 | base00: '#657b83',
8 | base0: '#839496',
9 | base1: '#93a1a1',
10 | base2: '#eee8d5',
11 | base3: '#fdf6e3',
12 | yellow: '#b58900',
13 | orange: '#cb4b16',
14 | red: '#dc322f',
15 | magenta: '#d33682',
16 | violet: '#6c71c4',
17 | blue: '#268bd2',
18 | cyan: '#2aa198',
19 | green: '#859900'
20 | }
21 |
22 | export const darkTheme: Theme = {
23 | rem(size: number): number {
24 | return Math.round(size * 16)
25 | },
26 |
27 | background: solarized.base03,
28 | text: solarized.base0,
29 | header: solarized.base1,
30 |
31 | button: solarized.base0,
32 | buttonText: solarized.base03,
33 |
34 | dropdown: solarized.base00,
35 | dropdownText: solarized.base03,
36 |
37 | textInput: solarized.base02,
38 | textInputBorder: solarized.base0,
39 | textInputText: solarized.base0,
40 |
41 | toast: solarized.base3,
42 | toastText: solarized.base00
43 | }
44 |
45 | export const lightTheme: Theme = {
46 | rem(size: number): number {
47 | return Math.round(size * 16)
48 | },
49 |
50 | background: solarized.base3,
51 | text: solarized.base00,
52 | header: solarized.base01,
53 |
54 | button: solarized.base2,
55 | buttonText: solarized.base00,
56 |
57 | dropdown: 'white',
58 | dropdownText: solarized.base03,
59 |
60 | textInput: solarized.base2,
61 | textInputBorder: solarized.base00,
62 | textInputText: solarized.base00,
63 |
64 | toast: solarized.base03,
65 | toastText: solarized.base1
66 | }
67 |
--------------------------------------------------------------------------------
/AirshipDemo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 |
5 | "esModuleInterop": true,
6 | "isolatedModules": true,
7 | "jsx": "react-native",
8 | "lib": ["es2017"],
9 | "moduleResolution": "node",
10 | "target": "esnext",
11 | "skipLibCheck": true,
12 |
13 | "strict": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # react-native-airship
2 |
3 | ## 0.2.12 (2023-07-07)
4 |
5 | - fixed: Make the barometer component non-accessible.
6 |
7 | ## 0.2.11 (2022-12-19)
8 |
9 | - fixed: Extra call to `handleLayout` within `Barometer` to fix edge-case for incorrect screen layout calculations.
10 |
11 | ## 0.2.10 (2022-09-16)
12 |
13 | - changed: Default to Default to `AirshipBridge` instead of `AirshipBridge`. This change may cause new TypeScript errors to appear, but it is not a breaking change - the cases were always broken, but TypeScript simply wasn't reporting the errors.
14 |
15 | ## 0.2.9 (2022-04-25)
16 |
17 | - added: Add `borderColor` and `borderWidth` props to the `AirshipModal` component.
18 |
19 | ## 0.2.8 (2022-04-01)
20 |
21 | - fixed: Replace deprecated `removeListener` calls.
22 |
23 | ## 0.2.7 (2021-07-29)
24 |
25 | - added: New optional `overflow` prop for `AirshipModal`.
26 |
27 | ## 0.2.6 (2021-07-15)
28 |
29 | - added: Add optional shadow properties to the `AirshipModal`:
30 | - `shadowOffset`
31 | - `shadowOpacity`
32 | - `shadowRadius`
33 |
34 | ## 0.2.5 (2021-04-15)
35 |
36 | - fixed: Make the `Airship` Flow type work like the Typescript version.
37 |
38 | ## 0.2.4 (2021-03-25)
39 |
40 | - fixed: Do not crash when calling `Airship.clear` recursively.
41 |
42 | ## 0.2.3 (2021-01-23)
43 |
44 | - fixed: Measure the screen by mounting a test component and seeing where it lands, avoiding the need for various properties to control the layout. If the status bar is translucent, or if they keyboard is `adjustPan` mode on Android, we can automatically determine that now and do the right thing.
45 | - removed: `avoidAndroidKeyboard` prop on the top-level `Airship`.
46 | - removed: `statusBarTranslucent` prop on the top-level `Airship`.
47 |
48 | ## 0.2.2 (2020-09-03)
49 |
50 | - added: Add an `Airship.clear` method, which calls any callbacks registered with `bridge.on('clear')`.
51 | - added: Add a `bridge.on('result')` method to replace `bridge.onResult`.
52 | - fixed: Add some missing Flow type definitions.
53 | - fixed: Make the Typescript definitions work better with strict mode.
54 | - deprecated: `bridge.onResult`
55 |
56 | ## 0.2.1 (2020-08-11)
57 |
58 | - Fix various Flow & documentation issues from the previous release.
59 |
60 | ## 0.2.0(2020-08-07)
61 |
62 | With this version, the demo components become an official part of the library. The old `react-native-airship/demos` entry point has gone away, so you can import `AirshipDropdown`, `AirshipModal`, and `AirshipToast` directly from `react-native-airship` now.
63 |
64 | Since the demo components are meant to be directly usable, they now accept many more properties for controlling their appearance & behavior.
65 |
66 | The `activity` property on the `AirshipToast` has also gone away. See the [documentation](./docs/toast.md) for another way to achieve this same result.
67 |
68 | The `styleOverride` property on the `AirshipModal` has also gone away. Use the new appearance properties to make changes now.
69 |
70 | ## 0.1.4 (2020-07-31)
71 |
72 | - Update the readme file with a cool image.
73 |
74 | ## 0.1.3 (2020-07-28)
75 |
76 | - Fix the Flow types for compatibility with older versions.
77 |
78 | ## 0.1.2 (2019-11-27)
79 |
80 | - Port the demos to `react-native-safe-area-context`.
81 | - Add a temporary `styleOverride` prop to the modal while we figure out which customizations we want to support.
82 |
83 | ## 0.1.1 (2019-11-22)
84 |
85 | - Fix a packaging glitch that made the demos unusable.
86 |
87 | ## 0.1.0 (2019-11-21)
88 |
89 | - Initial release, extracted from edge-react-gui.
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-airship
2 |
3 | The airship floats above your React Native application, providing a place for modals, alerts, menus, toasts, and anything else to appear on top of your normal UI.
4 |
5 |
6 |
7 | Unlike React Native's built-in `Modal` component, the airship doesn't block the user from interacting with the application below. The airship has also a simple, promised-based API that lets it easily host multiple children at once. To place an item on the airship, call the `Airship.show` method:
8 |
9 | ```javascript
10 | const answer = await Airship.show(bridge => (
11 |
12 | ))
13 | ```
14 |
15 | The `Airship.show` method returns a promise, so you can simply `await` the user's feedback. This is much simpler than the typical approach of setting up a router and writing custom state handling.
16 |
17 | Besides the generic `Airship` container, this library comes with a handful of ready-to-use UI components:
18 |
19 | - [AirshipDropdown](./docs/dropdown.md) - A drop-down alert.
20 | - [AirshipModal](./docs/modal.md) - A slide-up modal which dims the rest of the screen.
21 | - [AirshipToast](./docs/toast.md) - Emulates the Android Toast component in a cross-platform way.
22 |
23 | If these don't do what you want, you can easily [write your own components](./docs/custom-components.md) to work with Airship.
24 |
25 | ## Setup
26 |
27 | Install `react-native-airship` using either NPM or yarn:
28 |
29 | ```sh
30 | yarn add react-native-airship
31 | # or:
32 | npm i -s react-native-airship
33 | ```
34 |
35 | Next, create an Airship instance and place it outside your main scene or router:
36 |
37 | ```javascript
38 | // your-app.js:
39 |
40 | import { makeAirship } from 'react-native-airship'
41 |
42 | export const Airship = makeAirship()
43 |
44 | export const App = () => (
45 |
46 |
47 |
48 | )
49 | ```
50 |
51 | Now, anybody in your application can use this `Airship` instance to show things on top of the app:
52 |
53 | ```javascript
54 | import { Airship } from './your-app.js'
55 |
56 | Airship.show(bridge => )
57 | ```
58 |
59 | There is also an `Airship.clear` method that can quickly remove everything mounted on the Airship (useful when logging out, for instance).
60 |
61 | ## Demo
62 |
63 | This repository includes a [demo application](./AirshipDemo/) you can use to try out the Airship. You will need to run `yarn install` or `npm install` separately in that folder to set up the demo, and then run either `react-native run-android` or `react-native run-ios` to start the demo.
64 |
--------------------------------------------------------------------------------
/docs/custom-components.md:
--------------------------------------------------------------------------------
1 | # Custom Airship Components
2 |
3 | Although the Airship library comes with a handful of built-in components, it is actually super-easy to write your own:
4 |
5 | ```javascript
6 | function CustomFloatingComponent(props) {
7 | const { bridge } = props
8 | React.useEffect(() => bridge.on('clear', bridge.resolve), [])
9 | React.useEffect(() => bridge.on('result', bridge.remove), [])
10 |
11 | return (
12 | bridge.resolve()}>
13 | Tap to dismiss
14 |
15 | )
16 | }
17 |
18 | Airship.show(bridge => )
19 | ```
20 |
21 | This component is completely functional, although it lacks pretty styling. See [AirshipDemo/src/CustomFloatingComponent.tsx](../AirshipDemo/src/CustomFloatingComponent.tsx) for a styled version.
22 |
23 | Calling `Airship.show` is similar to calling `new Promise` - you receive some methods that you can use to control the resulting promise. The methods are placed on a `bridge` object, which makes them convenient to pass around as props.
24 |
25 | In this example, tapping the text calls `bridge.resolve` to resolve the promise. Once the promise resolves, the component un-mounts itself in response to the `bridge.on('result')` callback.
26 |
27 | ## Bridge Methods
28 |
29 | The `bridge` object has the following methods:
30 |
31 | - `bridge.resolve` - Resolves the promise returned from `Airship.show`.
32 | - `bridge.reject` - Rejects the promise returned from `Airship.show`.
33 | - `bridge.remove` - Removes the component from the Airship.
34 | - `bridge.on('result', callback)` - Invokes the callback when the component lifetime promise settles (either resolved or rejected).
35 | - `bridge.on('clear', callback)` - Invokes the callback whenever `Airship.clear` is called.
36 |
37 | A typical use-case is to use `bridge.on('result')` to start some sort of fade-out animation. That way, calling either `bridge.resolve` or `bridge.reject` will not only settle the promise, but will also begin hiding the component. Once the animation completes, call `bridge.remove` to finally un-mount the component.
38 |
39 | It is also a good idea to set up a `bridge.on('clear')` callback, so your component can remove itself when somebody calls `Airship.clear`.
40 |
41 | The bridge also has a deprecated `onResult` method, which is equivalent to calling `on('result')`. Do not use `onResult` method; it will go away in the next breaking release.
42 |
43 | ## Layout
44 |
45 | The `Airship` component mounts its children inside a wrapper component. This wrapper component fills the entire screen, and uses padding to avoid the status bar, keyboard, notches, and other obstacles.
46 |
47 | The wrapper component also has `flexDirection: 'row'` and `justifyContent: 'center'` set. This will center your floating UI horizontally. You can align your content vertically by setting `alignSelf` to either `flex-start` (top), `center`, or `flex-end` (bottom).
48 |
49 | The wrapper component's padding means that your floating UI will automatically avoid all the obstacles (such as notches) around the edges of the screen. On the other hand, this creates a problem if you actually _want_ your floating UI to touch one or more screen edges (such as for a drawer that slides in).
50 |
51 | There are two simple approaches to touching the screen edges:
52 |
53 | - Negative margin
54 | - Absolute positioning
55 |
56 | ### Negative Margin
57 |
58 | If you want your component to visually touch the screen edges while still keeping its contents in the safe area, just give those edges a negative margin:
59 |
60 | ```javascript
61 | return {...contents}
62 | ```
63 |
64 | By balancing the negative margin with an equal and opposite padding, the component will stretch outwards towards the edge of the screen while keeping its contents in the safe area. The exact amount of negative margin doesn't matter, as long as it is large enough to reach all the way to the edge (going over is fine). A conservative value like 64 is about 50% larger than the gap on an iPhone X, so it should be safe for most devices.
65 |
66 | ### Absolute Positioning
67 |
68 | You can also use absolute positioning if you simply want to cover the entire screen:
69 |
70 | ```javascript
71 | return (
72 |
73 | {...contents}
74 |
75 | )
76 | ```
77 |
78 | This will touch all four edges of the screen, completely ignoring the safe area. This approach is great for capturing touches or applying darkening outside your main UI. On the other hand, it loses the safe area information, which makes it less useful if you have contents that need to remain readable.
79 |
--------------------------------------------------------------------------------
/docs/dropdown.md:
--------------------------------------------------------------------------------
1 | # AirshipDropdown
2 |
3 | The dropdown component slides in from the top of the screen. It can have any contents you like, but it is most useful for alerts.
4 |
5 |
6 |
7 | The dropdown accepts a `bridge` property, which allows it to animate away & remove itself once the `Airship.show` promise resolves.
8 |
9 | The dropdown auto-hides itself after a short timeout, or whenever the user taps on it. You can pass an `onPress` callback to change the tap behavior, and you can use the `autoHideMs` prop to change the timeout (or disable it).
10 |
11 | ## Reference
12 |
13 | Here are the properties the component accepts. Only the `bridge` property is mandatory.
14 |
15 | ```typescript
16 | export interface AirshipDropdownProps {
17 | bridge: AirshipBridge
18 | children?: React.ReactNode
19 |
20 | // Called when the user taps anywhere in the dropdown.
21 | // Defaults to hiding the dropdown.
22 | onPress?: () => void
23 |
24 | // Determines how long the dropdown remains visible,
25 | // or 0 to disable auto-hide. Defaults to 5000ms.
26 | autoHideMs?: number
27 |
28 | // The component color. Defaults to white.
29 | backgroundColor?: string
30 |
31 | // The radius to use on the bottom corners. Defaults to 4.
32 | borderRadius?: number
33 |
34 | // The flex direction for the contents.
35 | flexDirection?: ViewStyle['flexDirection']
36 |
37 | // How to justify the contents along the flex direction.
38 | justifyContent?: ViewStyle['justifyContent']
39 |
40 | // The minimum gap between the component and the screen edges.
41 | // Takes 0-4 numbers (top, right, bottom, left),
42 | // using the same logic as the web `margin` property. Defaults to 0.
43 | margin?: number | number[]
44 |
45 | // The maximum height the component will be.
46 | // Defaults to 25% of the longest screen dimension.
47 | maxHeight?: number
48 |
49 | // The maximum width the component will be.
50 | // Defaults to 512.
51 | maxWidth?: number
52 |
53 | // Internal padding to place inside the component.
54 | // Takes 0-4 numbers (top, right, bottom, left),
55 | // using the same logic as the web `padding` property. Defaults to 0.
56 | padding?: number | number[]
57 |
58 | // How long the entry animation should be. Defaults to 300ms.
59 | slideInMs?: number
60 |
61 | // How long the exit animation should be. Defaults to 500ms.
62 | slideOutMs?: number
63 | }
64 | ```
65 |
--------------------------------------------------------------------------------
/docs/dropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/docs/dropdown.png
--------------------------------------------------------------------------------
/docs/isometric.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/docs/isometric.png
--------------------------------------------------------------------------------
/docs/isometric.svg:
--------------------------------------------------------------------------------
1 |
2 |
175 |
--------------------------------------------------------------------------------
/docs/modal.md:
--------------------------------------------------------------------------------
1 | # AirshipModal
2 |
3 | The modal component slides up from the bottom of the screen, while the rest of the app dims. Tapping outside the modal dismisses it. The modal can have any contents you like.
4 |
5 |
6 |
7 | The modal accepts a `bridge` property, which allows it to animate away & remove itself once the `Airship.show` promise resolves.
8 |
9 | The modal also requires an `onCancel` callback. The modal calls this whenever the user taps outside the modal or presses the phone's back button. The best approach is to call `bridge.resolve` or `bridge.reject` to dismiss the modal when this happens. If you don't want the user to easily escape the modal, just pass a do-nothing function to `onCancel`.
10 |
11 | The best way to use `AirshipModal` is to treat it as a building block for a larger modal component, which includes your various messages, buttons, or controls:
12 |
13 | ```javascript
14 | export function YesNoModal(props) {
15 | const { bridge, question } = props
16 |
17 | return (
18 | bridge.resolve(false)}>
19 | {question}
20 |
23 | )
24 | }
25 | ```
26 |
27 | Now you can pass this larger `YesNoModal` to the `Airship.show` method to get a quick boolean answer:
28 |
29 | ```javascript
30 | const answer = await Airship.show(bridge => (
31 |
32 | ))
33 | ```
34 |
35 | Although this example lacks pretty styling, it is completely functional. See [AirshipDemo/src/TextInputModal.tsx](../AirshipDemo/src/TextInputModal.tsx) for a text input modal with theming.
36 |
37 | ## Reference
38 |
39 | Here are the properties the component accepts. Only the `bridge` and `onCancel` properties are mandatory.
40 |
41 | ```typescript
42 | export interface AirshipModalProps {
43 | bridge: AirshipBridge
44 | children?: React.ReactNode
45 |
46 | // Called when the user taps outside the modal or clicks the back button:
47 | onCancel: () => void
48 |
49 | // True to have the modal float in the center of the screen,
50 | // or false for a bottom modal. Defaults to false.
51 | center?: boolean
52 |
53 | // The component color. Defaults to white.
54 | backgroundColor?: string
55 |
56 | // The radius to use on the corners. Defaults to 10.
57 | borderRadius?: number
58 |
59 | // The color to use for the border. Defaults to undefined.
60 | borderColor?: string
61 |
62 | // Width (better known as thickness) of the border. Default to 0
63 | borderWidth?: number
64 |
65 | // The flex direction for the contents.
66 | flexDirection?: ViewStyle['flexDirection']
67 |
68 | // How to justify the contents along the flex direction.
69 | justifyContent?: ViewStyle['justifyContent']
70 |
71 | // The minimum gap between the component and the screen edges.
72 | // Takes 0-4 numbers (top, right, bottom, left),
73 | // using the same logic as the web `margin` property. Defaults to 0.
74 | margin?: number | number[]
75 |
76 | // The maximum height the component will be.
77 | // Defaults to no limit.
78 | maxHeight?: number
79 |
80 | // The maximum width the component will be.
81 | // Defaults to 512.
82 | maxWidth?: number
83 |
84 | // Behavior for components that overflow the modal.
85 | // Defaults to 'visible'.
86 | overflow?: 'visible' | 'scroll' | 'hidden'
87 |
88 | // Internal padding to place inside the component.
89 | // Takes 0-4 numbers (top, right, bottom, left),
90 | // using the same logic as the web `padding` property. Defaults to 0.
91 | padding?: number | number[]
92 |
93 | // The offset for the drop shadow on iOS. Defaults to 0.
94 | shadowOffset?: { height: number; width: number }
95 |
96 | // The opacity of the drop shadow on iOS. Defaults to 1.
97 | shadowOpacity?: number
98 |
99 | // The blur radius of the drop shadow on iOS. Defaults to 10.
100 | shadowRadius?: number
101 |
102 | // How long the entry animation should be. Defaults to 300ms.
103 | slideInMs?: number
104 |
105 | // How long the exit animation should be. Defaults to 300ms.
106 | slideOutMs?: number
107 |
108 | // The color of the window underlay,
109 | // or a React element for a custom background.
110 | // Defaults to rgba(0, 0, 0, 0.75).
111 | underlay?: string | React.ReactElement
112 | }
113 | ```
114 |
--------------------------------------------------------------------------------
/docs/modal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/docs/modal.png
--------------------------------------------------------------------------------
/docs/toast.md:
--------------------------------------------------------------------------------
1 | # AirshipToast
2 |
3 | The AirshipToast emulates the Android Toast component in a cross-platform way.
4 |
5 |
6 |
7 | The toast accepts a `bridge` property, which allows it to animate away & remove itself once the `Airship.show` promise resolves.
8 |
9 | The toast ignores touch events (they pass right through), but it does auto-hide after a short timeout. You can use the `autoHideMs` prop to change the timeout (or disable it).
10 |
11 | Since the toast requires an `Airship` to be useful, a good approach is to wrap the toast in a short helper function:
12 |
13 | ```javascript
14 | import { AirshipToast, makeAirship } from 'react-native-airship'
15 |
16 | export const Airship = makeAirship()
17 |
18 | export function showToast(message) {
19 | Airship.show(bridge => )
20 | }
21 | ```
22 |
23 | Now your app can easily call `showToast` from anywhere.
24 |
25 | ## Advanced Example
26 |
27 | Here is a more advanced example that shows a spinner as long as a promise is pending:
28 |
29 | ```typescript
30 | export function showActivity(
31 | message: string,
32 | activity: Promise
33 | ): Promise {
34 | Airship.show(bridge => {
35 | // Hide the toast when the activity completes:
36 | activity.then(
37 | () => bridge.resolve(),
38 | () => bridge.resolve()
39 | )
40 | return (
41 |
42 |
43 |
44 | )
45 | })
46 | return activity
47 | }
48 | ```
49 |
50 | This is useful when a user-triggered action might take a long time, like fetching data from the network:
51 |
52 | ```javascript
53 | const response = await showActivity(
54 | 'Loading',
55 | fetch('https://google.com/robots.txt')
56 | )
57 | ```
58 |
59 | ## Reference
60 |
61 | Here are the properties the component accepts. Only the `bridge` property is mandatory. The defaults are designed to emulate the native Android toast.
62 |
63 | ```typescript
64 | export interface AirshipToastProps {
65 | bridge: AirshipBridge
66 | children?: React.ReactNode
67 |
68 | // A message to show inside the toast.
69 | // This will come before any other children.
70 | message?: string
71 |
72 | // Determines how long the dropdown remains visible,
73 | // or 0 to disable auto-hide. Defaults to 3000ms.
74 | autoHideMs?: number
75 |
76 | // The component background color.
77 | backgroundColor?: string
78 |
79 | // The radius to use on the corners.
80 | borderRadius?: number
81 |
82 | // How long the entry animation should be. Defaults to 300ms.
83 | fadeInMs?: number
84 |
85 | // How long the exit animation should be. Defaults to 500ms.
86 | fadeOutMs?: number
87 |
88 | // The minimum gap between the component and the screen edges.
89 | // Takes 0-4 numbers (top, right, bottom, left),
90 | // using the same logic as the web `margin` property.
91 | margin?: number | number[]
92 |
93 | // The maximum width the component will be.
94 | maxWidth?: number
95 |
96 | // The opacity the component should fade to.
97 | opacity?: number
98 |
99 | // Internal padding to place inside the component.
100 | // Takes 0-4 numbers (top, right, bottom, left),
101 | // using the same logic as the web `padding` property.
102 | padding?: number | number[]
103 |
104 | // The color to use for the text.
105 | textColor?: string
106 |
107 | // The size of the text.
108 | textSize?: number
109 | }
110 | ```
111 |
--------------------------------------------------------------------------------
/docs/toast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdgeApp/react-native-airship/ba98c206dca49b5fe8b7e19462eba3b73536bdbf/docs/toast.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-airship",
3 | "version": "0.2.12",
4 | "private": false,
5 | "description": "Flexible toolkit for building modals & alerts",
6 | "keywords": [
7 | "react native",
8 | "modal",
9 | "alert",
10 | "toast"
11 | ],
12 | "repository": "git@github.com:EdgeApp/react-native-airship",
13 | "license": "MIT",
14 | "author": "William Swanson ",
15 | "main": "lib/index.js",
16 | "types": "lib/index.d.ts",
17 | "files": [
18 | "/CHANGELOG.md",
19 | "/lib/*",
20 | "/package.json",
21 | "/README.md"
22 | ],
23 | "scripts": {
24 | "build.flow": "flow",
25 | "build.lib": "tsc && cp src/index.flow.js lib/index.js.flow",
26 | "clean": "rimraf lib",
27 | "fix": "npm run lint -- --fix",
28 | "lint": "eslint .",
29 | "precommit": "lint-staged && run-p build.* && (cd AirshipDemo; tsc)",
30 | "prepare": "husky install && npm-run-all clean -p build.*"
31 | },
32 | "lint-staged": {
33 | "*.{js,jsx,ts,tsx}": "eslint"
34 | },
35 | "dependencies": {
36 | "yavent": "^0.1.1"
37 | },
38 | "devDependencies": {
39 | "@types/react": "^17.0.43",
40 | "@types/react-native": "^0.67.3",
41 | "@typescript-eslint/eslint-plugin": "^4.8.2",
42 | "@typescript-eslint/parser": "^4.8.2",
43 | "babel-eslint": "^10.1.0",
44 | "eslint": "^7.14.0",
45 | "eslint-config-standard-kit": "0.15.1",
46 | "eslint-plugin-flowtype": "^5.2.0",
47 | "eslint-plugin-import": "^2.22.1",
48 | "eslint-plugin-prettier": "^3.1.4",
49 | "eslint-plugin-promise": "^4.2.1",
50 | "eslint-plugin-react": "^7.21.5",
51 | "eslint-plugin-react-hooks": "^4.2.0",
52 | "eslint-plugin-simple-import-sort": "^6.0.1",
53 | "flow-bin": "^0.132.0",
54 | "husky": "^7.0.0",
55 | "lint-staged": "^10.5.3",
56 | "npm-run-all": "^4.1.5",
57 | "prettier": "^2.2.0",
58 | "rimraf": "^3.0.2",
59 | "typescript": "^4.9.5"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/Airship.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { View } from 'react-native'
3 | import { makeEvent, makeEvents } from 'yavent'
4 |
5 | import {
6 | Airship,
7 | AirshipBridge,
8 | AirshipEvents,
9 | AirshipProps,
10 | AirshipRender
11 | } from '../types'
12 | import { sidesToOffset, sidesToPadding } from '../util/sides'
13 | import { Barometer, BarometerLayout } from './Barometer'
14 |
15 | interface Guest {
16 | key: string
17 | element: React.ReactNode
18 | }
19 |
20 | const emptyLayout: BarometerLayout = {
21 | offset: sidesToOffset([0, 0, 0, 0]),
22 | padding: sidesToPadding([0, 0, 0, 0])
23 | }
24 |
25 | /**
26 | * Constructs an Airship component.
27 | */
28 | export function makeAirship(): Airship {
29 | // Static state shared by all mounted containers:
30 | const [onClear, emitClear] = makeEvent()
31 | const [onGuestsChange, emitGuestsChange] = makeEvent()
32 | let guests: Guest[] = []
33 | let nextKey: number = 0
34 |
35 | const AirshipHost = (props: AirshipProps): JSX.Element => {
36 | const { children } = props
37 |
38 | // Watch the common guest list:
39 | const [ourGuests, setGuests] = React.useState(guests)
40 | React.useEffect(() => onGuestsChange(setGuests), [])
41 |
42 | // Track layout changes:
43 | const [layout, setLayout] = React.useState(emptyLayout)
44 |
45 | return (
46 | <>
47 |
48 | {children}
49 | {ourGuests.map(guest => (
50 |
61 | {guest.element}
62 |
63 | ))}
64 | >
65 | )
66 | }
67 |
68 | let clearing = false
69 | function clear(): void {
70 | if (clearing) return
71 | clearing = true
72 | emitClear(undefined)
73 | clearing = false
74 | }
75 |
76 | async function show(render: AirshipRender): Promise {
77 | const key = `airship${nextKey++}`
78 |
79 | function remove(): void {
80 | unclear()
81 | guests = guests.filter(guest => guest.key !== key)
82 | emitGuestsChange(guests)
83 | }
84 |
85 | // Assemble the bridge:
86 | const [on, emit] = makeEvents()
87 | let bridge!: AirshipBridge
88 | const promise = new Promise((resolve, reject) => {
89 | bridge = {
90 | on,
91 | onResult: callback => on('result', callback),
92 | reject,
93 | remove,
94 | resolve
95 | }
96 | })
97 |
98 | // Hook up events:
99 | promise.then(
100 | () => emit('result', undefined),
101 | () => emit('result', undefined)
102 | )
103 | const unclear = onClear(() => emit('clear', undefined))
104 |
105 | // Save the guest element in the shared state:
106 | guests = [...guests, { key, element: render(bridge) }]
107 | emitGuestsChange(guests)
108 | return promise
109 | }
110 |
111 | return Object.assign(AirshipHost, { clear, show })
112 | }
113 |
--------------------------------------------------------------------------------
/src/components/AirshipDropdown.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | Animated,
4 | Dimensions,
5 | TouchableWithoutFeedback,
6 | ViewStyle
7 | } from 'react-native'
8 |
9 | import { AirshipBridge } from '../types'
10 | import { fixSides, sidesToMargin, sidesToPadding } from '../util/sides'
11 |
12 | export interface AirshipDropdownProps {
13 | bridge: AirshipBridge
14 | children?: React.ReactNode
15 |
16 | // Called when the user taps anywhere in the dropdown.
17 | // Defaults to hiding the dropdown.
18 | onPress?: () => void
19 |
20 | // Determines how long the dropdown remains visible,
21 | // or 0 to disable auto-hide. Defaults to 5000ms.
22 | autoHideMs?: number
23 |
24 | // The component color. Defaults to white.
25 | backgroundColor?: string
26 |
27 | // The radius to use on the bottom corners. Defaults to 4.
28 | borderRadius?: number
29 |
30 | // The flex direction for the contents.
31 | flexDirection?: ViewStyle['flexDirection']
32 |
33 | // How to justify the contents along the flex direction.
34 | justifyContent?: ViewStyle['justifyContent']
35 |
36 | // The minimum gap between the component and the screen edges.
37 | // Takes 0-4 numbers (top, right, bottom, left),
38 | // using the same logic as the web `margin` property. Defaults to 0.
39 | margin?: number | number[]
40 |
41 | // The maximum height the component will be.
42 | // Defaults to 25% of the longest screen dimension.
43 | maxHeight?: number
44 |
45 | // The maximum width the component will be.
46 | // Defaults to 512.
47 | maxWidth?: number
48 |
49 | // Internal padding to place inside the component.
50 | // Takes 0-4 numbers (top, right, bottom, left),
51 | // using the same logic as the web `padding` property. Defaults to 0.
52 | padding?: number | number[]
53 |
54 | // How long the entry animation should be. Defaults to 300ms.
55 | slideInMs?: number
56 |
57 | // How long the exit animation should be. Defaults to 500ms.
58 | slideOutMs?: number
59 | }
60 |
61 | const safeAreaGap = 64
62 |
63 | /**
64 | * A notification that slides down from the top of the screen.
65 | */
66 | export function AirshipDropdown(props: AirshipDropdownProps): JSX.Element {
67 | const {
68 | bridge,
69 | children,
70 | onPress = () => bridge.resolve(undefined),
71 | autoHideMs = 5000,
72 | backgroundColor = 'white',
73 | borderRadius = 4,
74 | flexDirection,
75 | justifyContent,
76 | maxHeight = defaultMaxHeight(),
77 | maxWidth = 512,
78 | slideInMs = 300,
79 | slideOutMs = 500
80 | } = props
81 | const margin = sidesToMargin(fixSides(props.margin, 0))
82 | const padding = sidesToPadding(fixSides(props.padding, 0))
83 | const hiddenOffset = -(maxHeight + margin.marginBottom)
84 | margin.marginTop = -safeAreaGap
85 | padding.paddingTop += safeAreaGap
86 |
87 | // Create the animation:
88 | const offset = React.useRef(new Animated.Value(hiddenOffset)).current
89 | React.useEffect(() => {
90 | let timeout: ReturnType | undefined
91 |
92 | // Animate in:
93 | Animated.timing(offset, {
94 | toValue: 0,
95 | duration: slideInMs,
96 | useNativeDriver: true
97 | }).start(() => {
98 | // Start the auto-hide timer:
99 | if (autoHideMs > 0) {
100 | timeout = setTimeout(() => {
101 | timeout = undefined
102 | bridge.resolve(undefined)
103 | }, autoHideMs)
104 | }
105 | })
106 |
107 | // Animate out:
108 | bridge.on('clear', () => bridge.resolve(undefined))
109 | bridge.on('result', () => {
110 | Animated.timing(offset, {
111 | toValue: hiddenOffset,
112 | duration: slideOutMs,
113 | useNativeDriver: true
114 | }).start(() => bridge.remove())
115 | })
116 |
117 | return () => {
118 | if (timeout != null) clearTimeout(timeout)
119 | }
120 | // eslint-disable-next-line react-hooks/exhaustive-deps
121 | }, [])
122 |
123 | const bodyStyle: ViewStyle = {
124 | ...margin,
125 | ...padding,
126 | alignSelf: 'flex-start',
127 | backgroundColor,
128 | borderBottomLeftRadius: borderRadius,
129 | borderBottomRightRadius: borderRadius,
130 | flexDirection,
131 | flexShrink: 1,
132 | justifyContent,
133 | maxHeight,
134 | shadowOffset: { height: 0, width: 0 },
135 | shadowOpacity: 1,
136 | shadowRadius: 4,
137 | transform: [{ translateY: offset as any }],
138 | width: maxWidth // This works because flexShrink is set
139 | }
140 |
141 | return (
142 |
143 | {children}
144 |
145 | )
146 | }
147 |
148 | function defaultMaxHeight(): number {
149 | const { width, height } = Dimensions.get('screen')
150 | return 0.25 * Math.max(width, height)
151 | }
152 |
--------------------------------------------------------------------------------
/src/components/AirshipModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | Animated,
4 | BackHandler,
5 | Dimensions,
6 | TouchableWithoutFeedback,
7 | ViewStyle
8 | } from 'react-native'
9 |
10 | import { AirshipBridge } from '../types'
11 | import { fixSides, sidesToMargin, sidesToPadding } from '../util/sides'
12 |
13 | export interface AirshipModalProps {
14 | bridge: AirshipBridge
15 | children?: React.ReactNode
16 |
17 | // Called when the user taps outside the modal or clicks the back button:
18 | onCancel: () => void
19 |
20 | // True to have the modal float in the center of the screen,
21 | // or false for a bottom modal. Defaults to false.
22 | center?: boolean
23 |
24 | // The component color. Defaults to white.
25 | backgroundColor?: string
26 |
27 | // The radius to use on the corners. Defaults to 10.
28 | borderRadius?: number
29 |
30 | // The color to use for the border. Defaults to undefined.
31 | borderColor?: string
32 |
33 | // Width (better known as thickness) of the border. Default to 0
34 | borderWidth?: number
35 |
36 | // The flex direction for the contents.
37 | flexDirection?: ViewStyle['flexDirection']
38 |
39 | // How to justify the contents along the flex direction.
40 | justifyContent?: ViewStyle['justifyContent']
41 |
42 | // The minimum gap between the component and the screen edges.
43 | // Takes 0-4 numbers (top, right, bottom, left),
44 | // using the same logic as the web `margin` property. Defaults to 0.
45 | margin?: number | number[]
46 |
47 | // The maximum height the component will be.
48 | // Defaults to no limit.
49 | maxHeight?: number
50 |
51 | // The maximum width the component will be.
52 | // Defaults to 512.
53 | maxWidth?: number
54 |
55 | // Behavior for components that overflow the modal.
56 | // Defaults to 'visible'.
57 | overflow?: 'visible' | 'scroll' | 'hidden'
58 |
59 | // Internal padding to place inside the component.
60 | // Takes 0-4 numbers (top, right, bottom, left),
61 | // using the same logic as the web `padding` property. Defaults to 0.
62 | padding?: number | number[]
63 |
64 | // The offset for the drop shadow on iOS. Defaults to 0.
65 | shadowOffset?: { height: number; width: number }
66 |
67 | // The opacity of the drop shadow on iOS. Defaults to 1.
68 | shadowOpacity?: number
69 |
70 | // The blur radius of the drop shadow on iOS. Defaults to 10.
71 | shadowRadius?: number
72 |
73 | // How long the entry animation should be. Defaults to 300ms.
74 | slideInMs?: number
75 |
76 | // How long the exit animation should be. Defaults to 300ms.
77 | slideOutMs?: number
78 |
79 | // The color of the window underlay,
80 | // or a React element for a custom background.
81 | // Defaults to rgba(0, 0, 0, 0.75).
82 | underlay?: string | React.ReactElement
83 | }
84 |
85 | const safeAreaGap = 64
86 |
87 | /**
88 | * A modal that slides a modal up from the bottom of the screen
89 | * and dims the rest of the app.
90 | */
91 | export function AirshipModal(props: AirshipModalProps): JSX.Element {
92 | const {
93 | bridge,
94 | children,
95 | onCancel,
96 | backgroundColor = 'white',
97 | borderRadius = 10,
98 | borderColor,
99 | borderWidth = 0,
100 | center = false,
101 | flexDirection,
102 | justifyContent,
103 | maxHeight,
104 | maxWidth = 512,
105 | overflow = 'visible',
106 | shadowOffset = { height: 0, width: 0 },
107 | shadowOpacity = 1,
108 | shadowRadius = 10,
109 | slideInMs = 300,
110 | slideOutMs = 300,
111 | underlay = 'rgba(0, 0, 0, 0.75)'
112 | } = props
113 | const margin = sidesToMargin(fixSides(props.margin, 0))
114 | const padding = sidesToPadding(fixSides(props.padding, 0))
115 | React.useEffect(() => bridge.on('clear', onCancel), [bridge, onCancel])
116 |
117 | // Create the animations:
118 | const offset = React.useRef(
119 | new Animated.Value(Dimensions.get('window').height)
120 | ).current
121 | const opacity = React.useRef(new Animated.Value(0)).current
122 | React.useEffect(() => {
123 | // Animate in:
124 | Animated.parallel([
125 | Animated.timing(opacity, {
126 | toValue: 1,
127 | duration: slideInMs,
128 | useNativeDriver: true
129 | }),
130 | Animated.timing(offset, {
131 | toValue: 0,
132 | duration: slideInMs,
133 | useNativeDriver: true
134 | })
135 | ]).start()
136 |
137 | // Animate out:
138 | bridge.on('result', () => {
139 | Animated.parallel([
140 | Animated.timing(opacity, {
141 | toValue: 0,
142 | duration: slideOutMs,
143 | useNativeDriver: true
144 | }),
145 | Animated.timing(offset, {
146 | toValue: Dimensions.get('window').height,
147 | duration: slideOutMs,
148 | useNativeDriver: true
149 | })
150 | ]).start(bridge.remove)
151 | })
152 | // eslint-disable-next-line react-hooks/exhaustive-deps
153 | }, [])
154 |
155 | // Set up the back-button handler:
156 | React.useEffect(() => {
157 | const backHandler = BackHandler.addEventListener(
158 | 'hardwareBackPress',
159 | () => {
160 | onCancel()
161 | return true
162 | }
163 | )
164 | return () => backHandler.remove()
165 | }, [onCancel])
166 |
167 | const underlayStyle: ViewStyle = {
168 | backgroundColor: typeof underlay === 'string' ? underlay : 'transparent',
169 | bottom: 0,
170 | left: 0,
171 | opacity: opacity as any,
172 | position: 'absolute',
173 | right: 0,
174 | top: 0
175 | }
176 |
177 | const bodyCommon: ViewStyle = {
178 | ...margin,
179 | ...padding,
180 | alignSelf: center ? 'center' : 'flex-end',
181 | backgroundColor,
182 | flexDirection,
183 | flexShrink: 1,
184 | justifyContent,
185 | maxHeight,
186 | overflow,
187 | shadowOffset,
188 | shadowOpacity,
189 | shadowRadius,
190 | transform: [{ translateY: offset as any }],
191 | width: maxWidth // This works because flexShrink is set
192 | }
193 | const bodyStyle = center
194 | ? {
195 | ...bodyCommon,
196 | borderRadius,
197 | borderColor,
198 | borderWidth
199 | }
200 | : {
201 | ...bodyCommon,
202 | borderTopLeftRadius: borderRadius,
203 | borderTopRightRadius: borderRadius,
204 | borderColor,
205 | borderWidth,
206 | marginBottom: -safeAreaGap,
207 | paddingBottom: padding.paddingBottom + safeAreaGap
208 | }
209 |
210 | return (
211 | <>
212 | onCancel()}>
213 |
214 | {typeof underlay !== 'string' ? underlay : undefined}
215 |
216 |
217 | {children}
218 | >
219 | )
220 | }
221 |
--------------------------------------------------------------------------------
/src/components/AirshipToast.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Animated, Text, TextStyle, ViewStyle } from 'react-native'
3 |
4 | import { AirshipBridge } from '../types'
5 | import { fixSides, sidesToMargin, sidesToPadding } from '../util/sides'
6 |
7 | export interface AirshipToastProps {
8 | bridge: AirshipBridge
9 | children?: React.ReactNode
10 |
11 | // A message to show inside the toast.
12 | // This will come before any other children.
13 | message?: string
14 |
15 | // Determines how long the dropdown remains visible,
16 | // or 0 to disable auto-hide. Defaults to 3000ms.
17 | autoHideMs?: number
18 |
19 | // The component color. Defaults to grey.
20 | backgroundColor?: string
21 |
22 | // The radius to use on the corners.
23 | borderRadius?: number
24 |
25 | // How long the entry animation should be. Defaults to 300ms.
26 | fadeInMs?: number
27 |
28 | // How long the exit animation should be. Defaults to 500ms.
29 | fadeOutMs?: number
30 |
31 | // The minimum gap between the component and the screen edges.
32 | // Takes 0-4 numbers (top, right, bottom, left),
33 | // using the same logic as the web `margin` property.
34 | margin?: number | number[]
35 |
36 | // The maximum width the component will be.
37 | maxWidth?: number
38 |
39 | // The opacity the component should fade to. Defaults to 0.9.
40 | opacity?: number
41 |
42 | // Internal padding to place inside the component.
43 | // Takes 0-4 numbers (top, right, bottom, left),
44 | // using the same logic as the web `padding` property.
45 | padding?: number | number[]
46 |
47 | // The color to use for the text. Defaults to black.
48 | textColor?: string
49 |
50 | // The size of the text.
51 | textSize?: number
52 | }
53 |
54 | /**
55 | * A semi-transparent message overlay.
56 | */
57 | export function AirshipToast(props: AirshipToastProps): JSX.Element {
58 | const { textSize = 14 } = props
59 | const {
60 | autoHideMs = 3000,
61 | backgroundColor = 'white',
62 | borderRadius = 1.5 * textSize,
63 | bridge,
64 | children,
65 | fadeInMs = 300,
66 | fadeOutMs = 1000,
67 | maxWidth = 512,
68 | opacity: finalOpacity = 0.9,
69 | message,
70 | textColor = 'black'
71 | } = props
72 | const margin = sidesToMargin(fixSides(props.margin, 2 * textSize))
73 | const padding = sidesToPadding(fixSides(props.padding, textSize))
74 |
75 | // Create the animation:
76 | const opacity = React.useRef(new Animated.Value(0)).current
77 | React.useEffect(() => {
78 | let timeout: ReturnType | undefined
79 |
80 | // Animate in:
81 | Animated.timing(opacity, {
82 | toValue: finalOpacity,
83 | duration: fadeInMs,
84 | useNativeDriver: true
85 | }).start(() => {
86 | // Start the auto-hide timer:
87 | if (autoHideMs > 0) {
88 | timeout = setTimeout(() => {
89 | timeout = undefined
90 | bridge.resolve(undefined)
91 | }, autoHideMs)
92 | }
93 | })
94 |
95 | // Animate out:
96 | bridge.on('clear', () => bridge.resolve(undefined))
97 | bridge.on('result', () => {
98 | Animated.timing(opacity, {
99 | toValue: 0,
100 | duration: fadeOutMs,
101 | useNativeDriver: true
102 | }).start(() => bridge.remove())
103 | })
104 |
105 | return () => {
106 | if (timeout != null) clearTimeout(timeout)
107 | }
108 | })
109 |
110 | const bodyStyle: ViewStyle = {
111 | ...margin,
112 | ...padding,
113 | alignItems: 'center',
114 | alignSelf: 'flex-end',
115 | backgroundColor,
116 | borderRadius,
117 | flexDirection: 'row',
118 | justifyContent: 'flex-start',
119 | maxWidth,
120 | opacity: opacity as any
121 | }
122 |
123 | const textStyle: TextStyle = {
124 | color: textColor,
125 | flexShrink: 1,
126 | fontSize: textSize,
127 | textAlign: 'center'
128 | }
129 |
130 | return (
131 |
132 | {message != null ? {message} : null}
133 | {children}
134 |
135 | )
136 | }
137 |
--------------------------------------------------------------------------------
/src/components/Barometer.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | Dimensions,
4 | EmitterSubscription,
5 | Keyboard,
6 | KeyboardEventListener,
7 | Platform,
8 | SafeAreaView,
9 | StyleSheet,
10 | View
11 | } from 'react-native'
12 |
13 | import {
14 | addSides,
15 | mapSides,
16 | Offset,
17 | Padding,
18 | SideList,
19 | sidesToOffset,
20 | sidesToPadding,
21 | subtractSides
22 | } from '../util/sides'
23 |
24 | export interface BarometerLayout {
25 | offset: Offset
26 | padding: Padding
27 | }
28 |
29 | interface Props {
30 | onLayout?: (layout: BarometerLayout) => void
31 | }
32 |
33 | const emptySides: SideList = [0, 0, 0, 0]
34 |
35 | /**
36 | * Measures various things about the Airship environment,
37 | * so we know how to position our children.
38 | *
39 | * This component mounts a view with absolute positioning,
40 | * and then measures that view relative to the window.
41 | * If a side is inset from the window edge, we use a negative offset
42 | * to expand it outward. If a side extends beyond the window edge,
43 | * we use padding to push the content inward.
44 | *
45 | * On iOS, we also mount a child inside a SafeAreaView, to measure
46 | * the safe area insets. We add these to the padding.
47 | *
48 | * Finally, we keep track of the keyboard, adding extra padding &
49 | * scheduling animations as needed.
50 | */
51 | export function Barometer(props: Props): JSX.Element {
52 | const { onLayout = () => {} } = props
53 |
54 | // Mutable state:
55 | const keyboardHeight = React.useRef(0)
56 | const lastLayoutJson = React.useRef('')
57 | const view = React.useRef(null)
58 | const childView = React.useRef(null)
59 |
60 | // Handle layout changes:
61 | const handleLayout = React.useCallback((): void => {
62 | // Measure the view in the window:
63 | const viewPromise = new Promise(resolve => {
64 | if (view.current == null) return resolve(emptySides)
65 | view.current.measureInWindow((x, y, width, height) => {
66 | const window = Dimensions.get('window')
67 | resolve([y, window.width - width - x, window.height - height - y, x])
68 | })
69 | })
70 |
71 | // Measure the child view in the window:
72 | const childPromise = new Promise(resolve => {
73 | if (childView.current == null) return resolve(viewPromise)
74 | childView.current.measureInWindow((x, y, width, height) => {
75 | const window = Dimensions.get('window')
76 | resolve([y, window.width - width - x, window.height - height - y, x])
77 | })
78 | })
79 |
80 | // Measure the gap between the bottom of the screen and the view:
81 | const bottomPromise = new Promise(resolve => {
82 | if (view.current == null) return 0
83 | view.current.measure((x, y, width, height, screenX, screenY) => {
84 | const screen = Dimensions.get('screen')
85 | resolve(screen.height - height - screenY)
86 | })
87 | })
88 |
89 | // Combine the results, then call the callback:
90 | Promise.all([viewPromise, childPromise, bottomPromise])
91 | .then(([viewOffset, childOffset, bottomGap]) => {
92 | // Cancel out any offset, so we cover the full window:
93 | const offset = mapSides(viewOffset, side => -Math.max(side, 0))
94 |
95 | // If the offset is negative, issue positive padding,
96 | // plus any safe area:
97 | const safePadding = subtractSides(childOffset, viewOffset)
98 | const padding = addSides(
99 | safePadding,
100 | mapSides(viewOffset, side => Math.abs(side))
101 | )
102 |
103 | // Use the keyboard padding, if needed:
104 | const keyboardPadding = Math.max(
105 | keyboardHeight.current - bottomGap - offset[2],
106 | 0
107 | )
108 | padding[2] = Math.max(padding[2], keyboardPadding)
109 |
110 | // Send an update if we have changes:
111 | const string = JSON.stringify([offset, padding])
112 | if (string !== lastLayoutJson.current) {
113 | lastLayoutJson.current = string
114 | onLayout({
115 | offset: sidesToOffset(offset),
116 | padding: sidesToPadding(padding)
117 | })
118 |
119 | // Force a second trigger because React Native can fail to invoke
120 | // onLayout for some edge cases.
121 | setTimeout(handleLayout, 2000)
122 | }
123 | })
124 | .catch(() => {})
125 | }, [onLayout])
126 |
127 | // Subscribe to keyboard changes:
128 | React.useEffect(() => {
129 | const handleKeyboard: KeyboardEventListener = event => {
130 | const screen = Dimensions.get('screen')
131 | keyboardHeight.current = Math.min(
132 | // These two give different results sometimes, so pick the smaller one:
133 | screen.height - event.endCoordinates.screenY,
134 | event.endCoordinates.height
135 | )
136 | if (event.duration > 0) {
137 | Keyboard.scheduleLayoutAnimation(event)
138 | }
139 | handleLayout()
140 | }
141 | const listeners: EmitterSubscription[] = []
142 | if (Platform.OS === 'android') {
143 | listeners.push(Keyboard.addListener('keyboardDidShow', handleKeyboard))
144 | listeners.push(Keyboard.addListener('keyboardDidHide', handleKeyboard))
145 | } else {
146 | listeners.push(
147 | Keyboard.addListener('keyboardWillChangeFrame', handleKeyboard)
148 | )
149 | }
150 | return () => listeners.forEach(listener => listener.remove())
151 | }, [handleLayout])
152 |
153 | if (Platform.OS === 'android') {
154 | return (
155 |
162 | )
163 | }
164 |
165 | return (
166 |
173 |
174 |
175 | )
176 | }
177 |
--------------------------------------------------------------------------------
/src/index.flow.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import * as React from 'react'
4 | import { type OnEvents, type Unsubscribe } from 'yavent'
5 |
6 | export type { Unsubscribe }
7 |
8 | type AirshipEvents = {
9 | result: void,
10 | clear: void
11 | }
12 |
13 | /**
14 | * Control panel for managing a component inside an airship.
15 | */
16 | export type AirshipBridge = {
17 | // Use these to pass values to the outside world:
18 | +resolve: (value: T | Promise) => void,
19 | +reject: (error: Error) => void,
20 |
21 | // Unmounts the component:
22 | +remove: () => void,
23 |
24 | // Subscribes to events.
25 | // Use `on('result', callback)` to subscribe to
26 | // the promise being resolved or rejected.
27 | // Use `on('clear', callback)` to subscribe to
28 | // the `Airship.clear` method being called.
29 | +on: OnEvents,
30 |
31 | // Runs a callback when the result promise settles.
32 | // Deprecated in favor of `on('result')`.
33 | +onResult: (callback: () => mixed) => void
34 | }
35 |
36 | /**
37 | * Renders a component to place inside the airship.
38 | */
39 | type AirshipRender = (bridge: AirshipBridge) => React.Node
40 |
41 | /**
42 | * Props the Airship container component accepts.
43 | */
44 | export interface AirshipProps {
45 | children?: React.Node;
46 | }
47 |
48 | /**
49 | * The airship itself is a component you should mount around your main
50 | * scene or router.
51 | *
52 | * It has a static method anyone can call to display components.
53 | * The method returns a promise, which the component can use to pass values
54 | * to the outside world.
55 | */
56 | declare class AirshipClass extends React.Component {
57 | static clear(): void;
58 | static show(render: AirshipRender): Promise;
59 | }
60 |
61 | export type Airship = typeof AirshipClass
62 |
63 | /**
64 | * Constructs an Airship component.
65 | */
66 | declare export function makeAirship(): Airship
67 |
68 | type FlexDirection = 'column-reverse' | 'column' | 'row-reverse' | 'row'
69 | type JustifyContent =
70 | | 'center'
71 | | 'flex-end'
72 | | 'flex-start'
73 | | 'space-around'
74 | | 'space-between'
75 | | 'space-evenly'
76 |
77 | /**
78 | * A drop-down alert.
79 | */
80 | export type AirshipDropdownProps = {
81 | bridge: AirshipBridge,
82 | children?: React.Node,
83 | onPress?: () => void,
84 |
85 | autoHideMs?: number,
86 | backgroundColor?: string,
87 | borderRadius?: number,
88 | flexDirection?: FlexDirection,
89 | justifyContent?: JustifyContent,
90 | margin?: number | number[],
91 | maxHeight?: number,
92 | maxWidth?: number,
93 | padding?: number | number[],
94 | slideInMs?: number,
95 | slideOutMs?: number
96 | }
97 | declare export class AirshipDropdown
98 | extends React.Component {}
99 |
100 | /**
101 | * A slide-up modal which dims the rest of the screen.
102 | */
103 | export type AirshipModalProps = {
104 | bridge: AirshipBridge,
105 | children?: React.Node,
106 | onCancel: () => void,
107 | center?: boolean,
108 |
109 | backgroundColor?: string,
110 | borderRadius?: number,
111 | borderColor?: string,
112 | borderWidth?: number,
113 | flexDirection?: FlexDirection,
114 | justifyContent?: JustifyContent,
115 | margin?: number | number[],
116 | maxHeight?: number,
117 | maxWidth?: number,
118 | overflow?: 'visible' | 'scroll' | 'hidden',
119 | padding?: number | number[],
120 | shadowOffset?: { height: number, width: number },
121 | shadowOpacity?: number,
122 | shadowRadius?: number,
123 | slideInMs?: number,
124 | slideOutMs?: number,
125 | underlay?: string | React.Element
126 | }
127 | declare export class AirshipModal
128 | extends React.Component> {}
129 |
130 | /**
131 | * Emulates the Android Toast component in a cross-platform way.
132 | */
133 | export type AirshipToastProps = {
134 | bridge: AirshipBridge,
135 | children?: React.Node,
136 | message?: string,
137 |
138 | autoHideMs?: number,
139 | backgroundColor?: string,
140 | borderRadius?: number,
141 | fadeInMs?: number,
142 | fadeOutMs?: number,
143 | margin?: number | number[],
144 | maxWidth?: number,
145 | opacity?: number,
146 | padding?: number | number[],
147 | textColor?: string,
148 | textSize?: number
149 | }
150 | declare export class AirshipToast extends React.Component {}
151 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { Unsubscribe } from 'yavent'
2 |
3 | export { makeAirship } from './components/Airship'
4 | export {
5 | AirshipDropdown,
6 | AirshipDropdownProps
7 | } from './components/AirshipDropdown'
8 | export { AirshipModal, AirshipModalProps } from './components/AirshipModal'
9 | export { AirshipToast, AirshipToastProps } from './components/AirshipToast'
10 | export { Airship, AirshipBridge } from './types'
11 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { OnEvents } from 'yavent'
3 |
4 | export interface AirshipEvents {
5 | result: undefined
6 | clear: undefined
7 | }
8 |
9 | /**
10 | * Control panel for managing a component inside an airship.
11 | */
12 | export interface AirshipBridge {
13 | // Use these to pass values to the outside world:
14 | resolve: (value: T | PromiseLike) => void
15 | reject: (error: Error) => void
16 |
17 | // Unmounts the component:
18 | remove: () => void
19 |
20 | // Subscribes to events.
21 | // Use `on('result', callback)` to subscribe to
22 | // the promise being resolved or rejected.
23 | // Use `on('clear', callback)` to subscribe to
24 | // the `Airship.clear` method being called.
25 | on: OnEvents
26 |
27 | // Runs a callback when the result promise settles.
28 | // Deprecated in favor of `on('result')`.
29 | onResult: (callback: () => unknown) => void
30 | }
31 |
32 | /**
33 | * Renders a component to place inside the airship.
34 | */
35 | export type AirshipRender = (bridge: AirshipBridge) => React.ReactNode
36 |
37 | /**
38 | * Props the Airship container component accepts.
39 | */
40 | export interface AirshipProps {
41 | children?: React.ReactNode
42 | }
43 |
44 | /**
45 | * The Airship itself is a component you should mount around your main
46 | * scene or router.
47 | *
48 | * It has a static method anyone can call to display components.
49 | * The method returns a promise, which the component can use to pass values
50 | * to the outside world.
51 | */
52 | export interface Airship extends React.FunctionComponent {
53 | clear: () => void
54 |
55 | // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
56 | show: (render: AirshipRender) => Promise
57 | }
58 |
--------------------------------------------------------------------------------
/src/util/sides.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The four sides (top, right, bottom, left) as a tuple.
3 | */
4 | export type SideList = [number, number, number, number]
5 |
6 | export interface Margin {
7 | marginBottom: number
8 | marginLeft: number
9 | marginRight: number
10 | marginTop: number
11 | }
12 |
13 | export interface Offset {
14 | bottom: number
15 | left: number
16 | right: number
17 | top: number
18 | }
19 |
20 | export interface Padding {
21 | paddingBottom: number
22 | paddingLeft: number
23 | paddingRight: number
24 | paddingTop: number
25 | }
26 |
27 | /**
28 | * Interprets an array of 0-4 numbers as a web CSS sides shorthand
29 | * (top, right, bottom, left).
30 | */
31 | export function fixSides(
32 | sides: number[] | number | undefined,
33 | fallback: number
34 | ): SideList {
35 | if (sides == null) {
36 | return [fallback, fallback, fallback, fallback]
37 | }
38 | if (typeof sides === 'number') {
39 | return [sides, sides, sides, sides]
40 | }
41 |
42 | const top = sides[0] ?? fallback
43 | const right = sides[1] ?? top
44 | const bottom = sides[2] ?? top
45 | const left = sides[3] ?? right
46 | return [top, right, bottom, left]
47 | }
48 |
49 | export function addSides(a: SideList, b: SideList): SideList {
50 | return [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]]
51 | }
52 |
53 | export function subtractSides(a: SideList, b: SideList): SideList {
54 | return [a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]
55 | }
56 |
57 | export function mapSides(
58 | sides: SideList,
59 | f: (side: number) => number
60 | ): SideList {
61 | return [f(sides[0]), f(sides[1]), f(sides[2]), f(sides[3])]
62 | }
63 |
64 | /**
65 | * Turns a list of sides into CSS margin properties.
66 | */
67 | export function sidesToMargin(sides: SideList): Margin {
68 | return {
69 | marginTop: sides[0],
70 | marginRight: sides[1],
71 | marginBottom: sides[2],
72 | marginLeft: sides[3]
73 | }
74 | }
75 |
76 | /**
77 | * Turns a list of sides into CSS positioning properties.
78 | */
79 | export function sidesToOffset(sides: SideList): Offset {
80 | return {
81 | top: sides[0],
82 | right: sides[1],
83 | bottom: sides[2],
84 | left: sides[3]
85 | }
86 | }
87 |
88 | /**
89 | * Turns a list of sides into CSS padding properties.
90 | */
91 | export function sidesToPadding(sides: SideList): Padding {
92 | return {
93 | paddingTop: sides[0],
94 | paddingRight: sides[1],
95 | paddingBottom: sides[2],
96 | paddingLeft: sides[3]
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "declarationDir": "lib",
5 | "outDir": "lib",
6 |
7 | "esModuleInterop": true,
8 | "jsx": "react",
9 | "lib": ["es2015"],
10 | "module": "es2015",
11 | "moduleResolution": "node",
12 | "target": "es2015",
13 |
14 | "strict": true
15 | },
16 | "exclude": ["AirshipDemo", "lib"]
17 | }
18 |
--------------------------------------------------------------------------------
/update-demo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Copies the latest react-native-airship code changes into the demo folder
3 |
4 | if [ ! -d ./AirshipDemo/node_modules/react-native-airship/ ]; then
5 | echo 'Please run yarn inside the AirshipDemo folder first'
6 | exit 1
7 | fi
8 |
9 | npm run prepare
10 |
11 | rm -r ./AirshipDemo/node_modules/react-native-airship/lib/
12 | cp -r package.json lib ./AirshipDemo/node_modules/react-native-airship/
13 |
--------------------------------------------------------------------------------