@ionic/portals-react-native 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +67 -0
- package/README.md +192 -0
- package/ReactNativePortals.podspec +20 -0
- package/android/build.gradle +122 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +203 -0
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPackage.kt +20 -0
- package/ios/Podfile +14 -0
- package/ios/Podfile.lock +389 -0
- package/ios/PortalManager.m +14 -0
- package/ios/PortalView.m +14 -0
- package/ios/PortalsPubSub.m +16 -0
- package/ios/ReactNativePortals-Bridging-Header.h +3 -0
- package/ios/ReactNativePortals.swift +113 -0
- package/ios/ReactNativePortals.xcodeproj/project.pbxproj +417 -0
- package/ios/ReactNativePortals.xcodeproj/xcshareddata/xcschemes/ReactNativePortals.xcscheme +67 -0
- package/ios/ReactNativePortals.xcworkspace/contents.xcworkspacedata +10 -0
- package/ios/ReactNativePortals.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/lib/commonjs/PortalView.android.js +37 -0
- package/lib/commonjs/PortalView.android.js.map +1 -0
- package/lib/commonjs/PortalView.js +22 -0
- package/lib/commonjs/PortalView.js.map +1 -0
- package/lib/commonjs/index.js +62 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/PortalView.android.js +23 -0
- package/lib/module/PortalView.android.js.map +1 -0
- package/lib/module/PortalView.js +10 -0
- package/lib/module/PortalView.js.map +1 -0
- package/lib/module/index.js +30 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/PortalView.android.d.ts +4 -0
- package/lib/typescript/PortalView.d.ts +4 -0
- package/lib/typescript/index.d.ts +18 -0
- package/package.json +133 -0
- package/src/PortalView.android.tsx +31 -0
- package/src/PortalView.tsx +11 -0
- package/src/index.ts +53 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Ionic Portals License 1.0.0
|
|
2
|
+
|
|
3
|
+
Based on the text of the licenses in the Polyform Project with new account registration conditions.
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it, but only so long as you comply with the Account Registration Feature requirement below. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
|
|
12
|
+
|
|
13
|
+
## Account Registration Feature
|
|
14
|
+
|
|
15
|
+
You must preserve, and you must not remove or modify, the Account Registration Feature in the software.
|
|
16
|
+
|
|
17
|
+
## Distribution License
|
|
18
|
+
|
|
19
|
+
The licensor grants you an additional copyright license to distribute copies of the software, but only so long as you comply with the Account Registration Feature requirement above and the Notice requirement below. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
|
|
20
|
+
|
|
21
|
+
## Notices
|
|
22
|
+
|
|
23
|
+
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example:
|
|
24
|
+
|
|
25
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
26
|
+
|
|
27
|
+
## Changes and New Works License
|
|
28
|
+
|
|
29
|
+
The licensor grants you an additional copyright license to make changes and new works based on the software, but only so long as you comply with the Account Registration Feature requirement above.
|
|
30
|
+
|
|
31
|
+
## Patent License
|
|
32
|
+
|
|
33
|
+
The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
|
|
34
|
+
|
|
35
|
+
## Fair Use
|
|
36
|
+
|
|
37
|
+
You may have "fair use" rights for the software under the law. These terms do not limit them.
|
|
38
|
+
|
|
39
|
+
## No Other Rights
|
|
40
|
+
|
|
41
|
+
These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
|
|
42
|
+
|
|
43
|
+
## Patent Defense
|
|
44
|
+
|
|
45
|
+
If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
|
46
|
+
|
|
47
|
+
## Violations
|
|
48
|
+
|
|
49
|
+
The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 30 days of receiving notice. Otherwise, all your licenses end immediately.
|
|
50
|
+
|
|
51
|
+
## No Liability
|
|
52
|
+
|
|
53
|
+
***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
|
|
54
|
+
|
|
55
|
+
## Definitions
|
|
56
|
+
|
|
57
|
+
The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
|
|
58
|
+
|
|
59
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
60
|
+
|
|
61
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
62
|
+
|
|
63
|
+
**Your licenses** are all the licenses granted to you for the software under these terms.
|
|
64
|
+
|
|
65
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
|
66
|
+
|
|
67
|
+
**Account Registration Feature** is the portion of the software marked by the licensor as the Account Registration Feature in the software. The Account Registration Feature may be a license key, token, or any other such mechanism designated by the licensor.
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<br />
|
|
2
|
+
<div align="center">
|
|
3
|
+
<img src="https://user-images.githubusercontent.com/5769389/134952353-7d7b4145-3a80-4946-9b08-17b3a22c03a1.png" width="560" />
|
|
4
|
+
</div>
|
|
5
|
+
<div align="center">
|
|
6
|
+
⚡️ A supercharged native Web View for iOS and Android ⚡️
|
|
7
|
+
</div>
|
|
8
|
+
<br />
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/ionic-team/react-native-ionic-portals/actions?query=workflow%3ACI"><img src="https://img.shields.io/github/workflow/status/ionic-team/ionic-portals/CI?style=flat-square" /></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@ionic/react-native-portals"><img src="https://img.shields.io/npm/l/@ionic/react-native-portals?style=flat-square" /></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@ionic/react-native-portals"><img src="https://img.shields.io/npm/v/@ionic/react-native-portals?style=flat-square" /></a>
|
|
13
|
+
</p>
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://ionic.io/docs/portals"><img src="https://img.shields.io/static/v1?label=docs&message=ionic.io/portals&color=blue&style=flat-square" /></a>
|
|
16
|
+
<a href="https://twitter.com/ionicframework"><img src="https://img.shields.io/twitter/follow/ionicframework" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
Ionic Portals is a supercharged native Web View component for iOS and Android that lets you add web-based experiences to native mobile apps. It enables native and web teams to better collaborate and bring new and existing web experiences to mobile in a safe, controlled way.
|
|
22
|
+
|
|
23
|
+
## Getting Started
|
|
24
|
+
|
|
25
|
+
### Installation
|
|
26
|
+
`npm install @ionic/portals-react-native`
|
|
27
|
+
or
|
|
28
|
+
`yarn add @ionic/portals-react-native`
|
|
29
|
+
|
|
30
|
+
### Usage
|
|
31
|
+
Register Portals with your [product key](#Registration):
|
|
32
|
+
```javascript
|
|
33
|
+
import { register } from '@ionic/portals-react-native';
|
|
34
|
+
|
|
35
|
+
register('YOUR_PORTAL_KEY_HERE');
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Create a Portal and add it to the portal registry:
|
|
39
|
+
```javascript
|
|
40
|
+
import { addPortal } from '@ionic/portals-react-native';
|
|
41
|
+
const helloPortal = {
|
|
42
|
+
// A unique name to reference later
|
|
43
|
+
name: 'hello',
|
|
44
|
+
// This is the location of your web bundle relative to the asset directory in Android and Bundle.main in iOS
|
|
45
|
+
// This will default to the name of the portal
|
|
46
|
+
startDir: 'portals/hello',
|
|
47
|
+
// Any initial state to be provided to a Portal if needed
|
|
48
|
+
initialContext: {
|
|
49
|
+
greeting: 'Hello, world!'
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
addPortal(helloPortal);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Create a PortalView in your view hierarchy:
|
|
57
|
+
```javascript
|
|
58
|
+
import { PortalView } from '@ionic/portals-react-native';
|
|
59
|
+
|
|
60
|
+
<PortalView
|
|
61
|
+
// The name of the portal to be used in the view
|
|
62
|
+
name='hello'
|
|
63
|
+
|
|
64
|
+
// Set any initial context you may want to override.
|
|
65
|
+
initialContext={{ greeting: 'Goodbye!' }}
|
|
66
|
+
|
|
67
|
+
// Setting a size is required
|
|
68
|
+
style={{ flex: 1, height: 300 }}
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### iOS Specific Configuration
|
|
73
|
+
##### AppDelegate
|
|
74
|
+
Both Capacitor and React Native have classes named `AppDelegate`. To prevent a clash that can prevent your React Native application from launching,
|
|
75
|
+
you will need to rename `AppDelegate` to something else:
|
|
76
|
+
```objective-c
|
|
77
|
+
// AppDelegate.h
|
|
78
|
+
@interface RNAppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```objective-c
|
|
82
|
+
// AppDelegate.m
|
|
83
|
+
@implementation RNAppDelegate
|
|
84
|
+
@end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```objective-c
|
|
88
|
+
// main.m
|
|
89
|
+
#import <UIKit/UIKit.h>
|
|
90
|
+
|
|
91
|
+
#import "AppDelegate.h"
|
|
92
|
+
|
|
93
|
+
int main(int argc, char *argv[])
|
|
94
|
+
{
|
|
95
|
+
@autoreleasepool {
|
|
96
|
+
return UIApplicationMain(argc, argv, nil, NSStringFromClass([RNAppDelegate class]));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
##### Podfile
|
|
102
|
+
Because many of the Ionic Portals dependencies are comprised of Swift code and have custom module maps, you will need to add `use_frameworks!` to your iOS Podfile and remove `use_flipper!()`
|
|
103
|
+
|
|
104
|
+
### Communicating between React Native and Web
|
|
105
|
+
One of the key features of Ionic Portals for React Native is facilitating communication between the web and React Native layers of your application.
|
|
106
|
+
Publishing a message to the web:
|
|
107
|
+
```javascript
|
|
108
|
+
import { publish } from '@ionic/portals-react-native';
|
|
109
|
+
|
|
110
|
+
publish('topic', { number: 1 })
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Subscribe to messages from the web:
|
|
114
|
+
```javascript
|
|
115
|
+
import { subscribe } from '@ionic/portals-react-native';
|
|
116
|
+
|
|
117
|
+
let subscriptionReference = await subscribe('topic', message => {
|
|
118
|
+
// Here you have access to:
|
|
119
|
+
// message.data - Any data sent from the web
|
|
120
|
+
// message.subscriptionRef - The subscription reference used to manage the lifecycle of the subscription
|
|
121
|
+
// message.topic - The topic the message was published on
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
When you no longer need to receive events, unsubscribe:
|
|
126
|
+
```javascript
|
|
127
|
+
import { unsubscribe } from '@ionic/portals-react-native';
|
|
128
|
+
|
|
129
|
+
unsubscribe('channel:topic', subscriptionReference)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
To see an example of Portals Pub/Sub in action that manages the lifecycle of a subscription with the lifecycle of a React Native component, refer to the [`PubSubLabel`](https://github.com/ionic-team/react-native-ionic-portals/blob/af19df0d66059d85ab8dde493504368c3bf39127/example/App.tsx#L53) implementation in the example project.
|
|
133
|
+
|
|
134
|
+
### Using Capacitor Plugins
|
|
135
|
+
If you need to use any Capacitor plugins, you will have to register them in your Android project. This will also require creating and registering your Portals in native code as well:
|
|
136
|
+
|
|
137
|
+
**Android**
|
|
138
|
+
```java
|
|
139
|
+
public class MainApplication extends Application implements ReactApplication {
|
|
140
|
+
@Override
|
|
141
|
+
public void onCreate() {
|
|
142
|
+
super.onCreate();
|
|
143
|
+
|
|
144
|
+
PortalManager.register("YOUR_PORTAL_KEY_HERE");
|
|
145
|
+
PortalManager.newPortal("hello")
|
|
146
|
+
.addPlugin(MyCapacitorPlugin.class) // Plugin registration
|
|
147
|
+
.setInitialContext(Map.of("greeting", "Hello, world!"))
|
|
148
|
+
.setStartDir("portals/hello")
|
|
149
|
+
.create();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**iOS**
|
|
155
|
+
```objective-c
|
|
156
|
+
@implementation RNAppDelegate
|
|
157
|
+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDicationary *)launchOptions {
|
|
158
|
+
// React Native boilerplate
|
|
159
|
+
[PortalManager register:@"YOUR_PORTAL_KEY_HERE"];
|
|
160
|
+
PortalBuilder *builder = [[PortalBuilder alloc] init:@"hello"];
|
|
161
|
+
[builder setStartDir:@"portals/hello"];
|
|
162
|
+
[builder setInitialContext: @{ @"greeting": @"Hello, world!" }]
|
|
163
|
+
Portal *portal = [builder create];
|
|
164
|
+
[PortalManager addPortal:portal];
|
|
165
|
+
}
|
|
166
|
+
@end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Bundling Your Web Apps
|
|
170
|
+
Currently there is no tooling for bundling your web apps directly as part of @ionic/portals-react-native. Please follow the [native guides](https://ionic.io/docs/portals/how-to/pull-in-web-bundle#setup-the-web-asset-directory) to manage this as part of the native build process.
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
## Registration
|
|
174
|
+
|
|
175
|
+
Ionic Portals for React Native requires a key to use. Once you have integrated Portals into your project, login to your ionic account to get a key. See our doc on [how to register for free and get your Portals license key](https://ionic.io/docs/portals/how-to/get-a-product-key) and refer to the [usage](#Usage) section on how to add your key to your React Native application.
|
|
176
|
+
|
|
177
|
+
## FAQ
|
|
178
|
+
|
|
179
|
+
### What is the pricing for Portals use?
|
|
180
|
+
|
|
181
|
+
Portals is free to use in non-production environments. Businesses with more than USD $1 million in annual revenue are required to purchase a license from Ionic before using Portals in production.
|
|
182
|
+
|
|
183
|
+
### Is Portals Open Source?
|
|
184
|
+
|
|
185
|
+
See our [license](https://github.com/ionic-team/ionic-portals/blob/main/LICENSE.md).
|
|
186
|
+
|
|
187
|
+
### How is Portals Related to Capacitor and Ionic?
|
|
188
|
+
|
|
189
|
+
Ionic Portals is a solution that lets you add web-based experiences to your native mobile apps. Portals uses [Capacitor](https://capacitorjs.com) as a bridge between the native code and the web code to allow for cross-communication between the two layers. Because Portals uses Capacitor under the hood, you are able to use any existing [Capacitor Plugins](https://capacitorjs.com/docs/plugins) and even most [Cordova Plugins](https://capacitorjs.com/docs/plugins/cordova) while continuing to use your existing native workflow. Portals for React Native brings these capabilities to React Native applications.
|
|
190
|
+
|
|
191
|
+
[Ionic Framework](https://ionicframework.com/) is the open-source mobile app development framework that makes it easy to build top quality native and progressive web apps with web technologies. Your web experiences can be developed with Ionic, but it is not necessary to use Portals.
|
|
192
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "ReactNativePortals"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => "14.0" }
|
|
14
|
+
s.source = { :git => "https://github.com/ionic-team/react-native-ionic-portals.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
|
|
18
|
+
s.dependency "React-Core"
|
|
19
|
+
s.dependency "IonicPortals", "~> 0.5.1"
|
|
20
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
|
+
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['ReactNativePortals_kotlinVersion']
|
|
4
|
+
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
dependencies {
|
|
11
|
+
classpath 'com.android.tools.build:gradle:7.1.2'
|
|
12
|
+
// noinspection DifferentKotlinGradleVersion
|
|
13
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
apply plugin: 'com.android.library'
|
|
18
|
+
apply plugin: 'kotlin-android'
|
|
19
|
+
|
|
20
|
+
def getExtOrIntegerDefault(name) {
|
|
21
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativePortals_' + name]).toInteger()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
android {
|
|
25
|
+
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion 16
|
|
28
|
+
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
|
|
29
|
+
versionCode 1
|
|
30
|
+
versionName "1.0"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
buildTypes {
|
|
34
|
+
release {
|
|
35
|
+
minifyEnabled false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
lintOptions {
|
|
39
|
+
disable 'GradleCompatible'
|
|
40
|
+
}
|
|
41
|
+
compileOptions {
|
|
42
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
43
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
repositories {
|
|
48
|
+
mavenCentral()
|
|
49
|
+
jcenter()
|
|
50
|
+
google()
|
|
51
|
+
|
|
52
|
+
def found = false
|
|
53
|
+
def defaultDir = null
|
|
54
|
+
def androidSourcesName = 'React Native sources'
|
|
55
|
+
|
|
56
|
+
if (rootProject.ext.has('reactNativeAndroidRoot')) {
|
|
57
|
+
defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
|
|
58
|
+
} else {
|
|
59
|
+
defaultDir = new File(
|
|
60
|
+
projectDir,
|
|
61
|
+
'/../../../node_modules/react-native/android'
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (defaultDir.exists()) {
|
|
66
|
+
maven {
|
|
67
|
+
url defaultDir.toString()
|
|
68
|
+
name androidSourcesName
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
|
|
72
|
+
found = true
|
|
73
|
+
} else {
|
|
74
|
+
def parentDir = rootProject.projectDir
|
|
75
|
+
|
|
76
|
+
1.upto(5, {
|
|
77
|
+
if (found) return true
|
|
78
|
+
parentDir = parentDir.parentFile
|
|
79
|
+
|
|
80
|
+
def androidSourcesDir = new File(
|
|
81
|
+
parentDir,
|
|
82
|
+
'node_modules/react-native'
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def androidPrebuiltBinaryDir = new File(
|
|
86
|
+
parentDir,
|
|
87
|
+
'node_modules/react-native/android'
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if (androidPrebuiltBinaryDir.exists()) {
|
|
91
|
+
maven {
|
|
92
|
+
url androidPrebuiltBinaryDir.toString()
|
|
93
|
+
name androidSourcesName
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
|
|
97
|
+
found = true
|
|
98
|
+
} else if (androidSourcesDir.exists()) {
|
|
99
|
+
maven {
|
|
100
|
+
url androidSourcesDir.toString()
|
|
101
|
+
name androidSourcesName
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
|
|
105
|
+
found = true
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!found) {
|
|
111
|
+
throw new GradleException(
|
|
112
|
+
"${project.name}: unable to locate React Native android sources. " +
|
|
113
|
+
"Ensure you have you installed React Native as a dependency in your project and try again."
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
dependencies {
|
|
119
|
+
// noinspection GradleDynamicVersion
|
|
120
|
+
api 'com.facebook.react:react-native:+'
|
|
121
|
+
implementation "io.ionic:portals:0.5.0"
|
|
122
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
package io.ionic.portals.reactnative
|
|
2
|
+
|
|
3
|
+
import android.view.Choreographer
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import android.widget.FrameLayout
|
|
7
|
+
import androidx.fragment.app.FragmentActivity
|
|
8
|
+
import com.facebook.react.bridge.*
|
|
9
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
10
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
12
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
13
|
+
import com.getcapacitor.JSObject
|
|
14
|
+
import io.ionic.portals.*
|
|
15
|
+
import org.json.JSONArray
|
|
16
|
+
import org.json.JSONException
|
|
17
|
+
import org.json.JSONObject
|
|
18
|
+
|
|
19
|
+
class PortalManagerModule(reactContext: ReactApplicationContext) :
|
|
20
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
21
|
+
override fun getName(): String {
|
|
22
|
+
return "IONPortalManager"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@ReactMethod
|
|
26
|
+
fun register(key: String) {
|
|
27
|
+
PortalManager.register(key)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@ReactMethod
|
|
31
|
+
fun addPortal(map: ReadableMap) {
|
|
32
|
+
map.getString("name")?.let { name ->
|
|
33
|
+
val startDir = map.getString("startDir")
|
|
34
|
+
val initialContext = map.getMap("initialContext")?.toHashMap()
|
|
35
|
+
|
|
36
|
+
val portalBuilder = PortalBuilder(name)
|
|
37
|
+
|
|
38
|
+
if (startDir != null) {
|
|
39
|
+
portalBuilder.setStartDir(startDir)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (initialContext != null) {
|
|
43
|
+
portalBuilder.setInitialContext(initialContext)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// TODO: We need to figure out if we can register plugins from javascript
|
|
47
|
+
val portal = portalBuilder
|
|
48
|
+
.addPlugin(PortalsPlugin::class.java)
|
|
49
|
+
.create()
|
|
50
|
+
|
|
51
|
+
PortalManager.addPortal(portal)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class PortalsPubSubModule(reactContext: ReactApplicationContext) :
|
|
57
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
58
|
+
override fun getName(): String {
|
|
59
|
+
return "IONPortalsPubSub"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@ReactMethod
|
|
63
|
+
fun subscribe(topic: String, promise: Promise) {
|
|
64
|
+
val reference = PortalsPlugin.subscribe(topic) { result ->
|
|
65
|
+
reactApplicationContext
|
|
66
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
67
|
+
.emit("PortalsSubscription", result.toJSObject().toReactMap())
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
promise.resolve(reference)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@ReactMethod
|
|
74
|
+
fun publish(topic: String, data: ReadableMap) {
|
|
75
|
+
PortalsPlugin.publish(topic, data.toJSObject())
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@ReactMethod
|
|
79
|
+
fun unsubscribe(topic: String, reference: Int) {
|
|
80
|
+
PortalsPlugin.unsubscribe(topic, reference)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// These are required to be an EventEmitter in javascript
|
|
84
|
+
|
|
85
|
+
@ReactMethod
|
|
86
|
+
fun addListener(eventName: String) {
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@ReactMethod
|
|
90
|
+
fun removeListeners(count: Int) {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fun JSONObject.toReactMap(): ReadableMap =
|
|
95
|
+
keys().asSequence().fold(WritableNativeMap()) { map, key ->
|
|
96
|
+
try {
|
|
97
|
+
when (val value = get(key)) {
|
|
98
|
+
is JSONObject -> map.putMap(key, value.toReactMap())
|
|
99
|
+
is JSONArray -> map.putArray(key, value.toReactArray())
|
|
100
|
+
is Boolean -> map.putBoolean(key, value)
|
|
101
|
+
is Int -> map.putInt(key, value)
|
|
102
|
+
is Double -> map.putDouble(key, value)
|
|
103
|
+
is String -> map.putString(key, value)
|
|
104
|
+
null -> map.putNull(key)
|
|
105
|
+
else -> map.putString(key, value.toString())
|
|
106
|
+
}
|
|
107
|
+
} catch (_: JSONException) {
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return@fold map
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fun JSONArray.toReactArray(): ReadableArray =
|
|
114
|
+
(0 until length()).fold(WritableNativeArray()) { array, index ->
|
|
115
|
+
try {
|
|
116
|
+
when (val value = get(index)) {
|
|
117
|
+
is JSONObject -> array.pushMap(value.toReactMap())
|
|
118
|
+
is JSONArray -> array.pushArray(value.toReactArray())
|
|
119
|
+
is Boolean -> array.pushBoolean(value)
|
|
120
|
+
is Int -> array.pushInt(value)
|
|
121
|
+
is Double -> array.pushDouble(value)
|
|
122
|
+
is String -> array.pushString(value)
|
|
123
|
+
null -> array.pushNull()
|
|
124
|
+
else -> array.pushString(value.toString())
|
|
125
|
+
}
|
|
126
|
+
} catch (_: JSONException) {
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return@fold array
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fun ReadableMap.toJSObject(): JSObject = JSObject.fromJSONObject(JSONObject(toHashMap()))
|
|
133
|
+
|
|
134
|
+
class PortalViewManager(private val context: ReactApplicationContext) :
|
|
135
|
+
ViewGroupManager<FrameLayout>() {
|
|
136
|
+
private val createId = 1
|
|
137
|
+
private var portal: Portal? = null
|
|
138
|
+
|
|
139
|
+
@ReactProp(name = "name")
|
|
140
|
+
fun setPortal(viewGroup: ViewGroup, portalName: String) {
|
|
141
|
+
portal = PortalManager.getPortal(portalName)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@ReactProp(name = "initialContext")
|
|
145
|
+
fun setInitialContext(viewGroup: ViewGroup, initialContext: ReadableMap) {
|
|
146
|
+
portal?.setInitialContext(initialContext.toHashMap())
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override fun getName(): String {
|
|
150
|
+
return "AndroidPortalView"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
154
|
+
return FrameLayout(reactContext)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
override fun getCommandsMap(): MutableMap<String, Int>? {
|
|
158
|
+
return mutableMapOf("create" to createId)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
override fun receiveCommand(root: FrameLayout, commandId: String?, args: ReadableArray?) {
|
|
162
|
+
super.receiveCommand(root, commandId, args)
|
|
163
|
+
val viewId = args?.getInt(0) ?: return
|
|
164
|
+
val commandId = commandId?.toIntOrNull() ?: return
|
|
165
|
+
|
|
166
|
+
when (commandId) {
|
|
167
|
+
createId -> createFragment(root, viewId)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private fun createFragment(root: FrameLayout, viewId: Int) {
|
|
172
|
+
val portal = portal ?: return
|
|
173
|
+
val parentView = root.findViewById<ViewGroup>(viewId)
|
|
174
|
+
setupLayout(parentView)
|
|
175
|
+
|
|
176
|
+
val portalFragment = PortalFragment(portal)
|
|
177
|
+
val fragmentActivity = context.currentActivity as? FragmentActivity ?: return
|
|
178
|
+
fragmentActivity.supportFragmentManager
|
|
179
|
+
.beginTransaction()
|
|
180
|
+
.replace(viewId, portalFragment, "$viewId")
|
|
181
|
+
.commit()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private fun setupLayout(view: ViewGroup) {
|
|
185
|
+
Choreographer.getInstance().postFrameCallback {
|
|
186
|
+
layoutChildren(view)
|
|
187
|
+
view.viewTreeObserver.dispatchOnGlobalLayout()
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private fun layoutChildren(view: ViewGroup) {
|
|
192
|
+
for (i in 0 until view.childCount) {
|
|
193
|
+
val child = view.getChildAt(i)
|
|
194
|
+
|
|
195
|
+
child.measure(
|
|
196
|
+
View.MeasureSpec.makeMeasureSpec(view.measuredWidth, View.MeasureSpec.EXACTLY),
|
|
197
|
+
View.MeasureSpec.makeMeasureSpec(view.measuredHeight, View.MeasureSpec.EXACTLY)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package io.ionic.portals.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ReactNativePortalsPackage : ReactPackage {
|
|
10
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
11
|
+
return listOf(
|
|
12
|
+
PortalManagerModule(reactContext),
|
|
13
|
+
PortalsPubSubModule(reactContext)
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
18
|
+
return listOf(PortalViewManager(reactContext))
|
|
19
|
+
}
|
|
20
|
+
}
|
package/ios/Podfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
|
2
|
+
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
|
3
|
+
|
|
4
|
+
platform :ios, '14.0'
|
|
5
|
+
|
|
6
|
+
target 'ReactNativePortals' do
|
|
7
|
+
# Comment the next line if you don't want to use dynamic frameworks
|
|
8
|
+
config = use_native_modules!
|
|
9
|
+
use_frameworks!
|
|
10
|
+
use_react_native!(:path => config["reactNativePath"])
|
|
11
|
+
# Pods for ReactNativePortals
|
|
12
|
+
pod 'IonicPortals', '~> 0.5.1'
|
|
13
|
+
end
|
|
14
|
+
|