cobrowse-sdk-react-native 2.15.0 → 2.17.0
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/.github/workflows/lint.yaml +0 -2
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/cobrowse/reactnative/CobrowseIOModule.java +36 -0
- package/cobrowse-sdk-react-native.podspec +1 -1
- package/ios/RCTCobrowseIO.m +8 -0
- package/js/CBIOBroadcastPickerView.tsx +15 -0
- package/js/CobrowseAccessibilityService.ts +15 -0
- package/js/CobrowseIO.ts +161 -0
- package/js/CobrowseView.tsx +150 -0
- package/js/{Redacted.js → Redacted.tsx} +3 -2
- package/js/Session.ts +128 -0
- package/js/SessionControl.ts +38 -0
- package/js/Unredacted.tsx +106 -0
- package/js/{index.js → index.ts} +1 -0
- package/js/{useSession.js → useSession.ts} +5 -4
- package/lib/commonjs/CBIOBroadcastPickerView.js +15 -0
- package/lib/commonjs/CBIOBroadcastPickerView.js.map +1 -0
- package/lib/commonjs/CobrowseAccessibilityService.js +22 -0
- package/lib/commonjs/CobrowseAccessibilityService.js.map +1 -0
- package/lib/commonjs/CobrowseIO.js +154 -0
- package/lib/commonjs/CobrowseIO.js.map +1 -0
- package/lib/commonjs/CobrowseView.js +146 -0
- package/lib/commonjs/CobrowseView.js.map +1 -0
- package/lib/commonjs/Redacted.js +14 -0
- package/lib/commonjs/Redacted.js.map +1 -0
- package/lib/commonjs/Session.js +100 -0
- package/lib/commonjs/Session.js.map +1 -0
- package/lib/commonjs/SessionControl.js +39 -0
- package/lib/commonjs/SessionControl.js.map +1 -0
- package/lib/commonjs/Unredacted.js +82 -0
- package/lib/commonjs/Unredacted.js.map +1 -0
- package/lib/commonjs/index.js +77 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/useSession.js +35 -0
- package/lib/commonjs/useSession.js.map +1 -0
- package/lib/module/CBIOBroadcastPickerView.js +8 -0
- package/lib/module/CBIOBroadcastPickerView.js.map +1 -0
- package/lib/module/CobrowseAccessibilityService.js +13 -0
- package/lib/module/CobrowseAccessibilityService.js.map +1 -0
- package/lib/module/CobrowseIO.js +146 -0
- package/lib/module/CobrowseIO.js.map +1 -0
- package/lib/module/CobrowseView.js +136 -0
- package/lib/module/CobrowseView.js.map +1 -0
- package/lib/module/Redacted.js +7 -0
- package/lib/module/Redacted.js.map +1 -0
- package/lib/module/Session.js +93 -0
- package/lib/module/Session.js.map +1 -0
- package/lib/module/SessionControl.js +31 -0
- package/lib/module/SessionControl.js.map +1 -0
- package/lib/module/Unredacted.js +71 -0
- package/lib/module/Unredacted.js.map +1 -0
- package/lib/module/index.js +9 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/useSession.js +28 -0
- package/lib/module/useSession.js.map +1 -0
- package/lib/typescript/CBIOBroadcastPickerView.d.ts +8 -0
- package/lib/typescript/CBIOBroadcastPickerView.d.ts.map +1 -0
- package/lib/typescript/CobrowseAccessibilityService.d.ts +8 -0
- package/lib/typescript/CobrowseAccessibilityService.d.ts.map +1 -0
- package/lib/typescript/CobrowseIO.d.ts +31 -0
- package/lib/typescript/CobrowseIO.d.ts.map +1 -0
- package/lib/typescript/CobrowseView.d.ts +25 -0
- package/lib/typescript/CobrowseView.d.ts.map +1 -0
- package/lib/typescript/Redacted.d.ts +4 -0
- package/lib/typescript/Redacted.d.ts.map +1 -0
- package/lib/typescript/Session.d.ts +42 -0
- package/lib/typescript/Session.d.ts.map +1 -0
- package/lib/typescript/SessionControl.d.ts +17 -0
- package/lib/typescript/SessionControl.d.ts.map +1 -0
- package/lib/typescript/Unredacted.d.ts +10 -0
- package/lib/typescript/Unredacted.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +10 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/useSession.d.ts +3 -0
- package/lib/typescript/useSession.d.ts.map +1 -0
- package/package.json +25 -4
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +28 -0
- package/js/CBIOBroadcastPickerView.js +0 -11
- package/js/CobrowseAccessibilityService.js +0 -11
- package/js/CobrowseIO.js +0 -145
- package/js/CobrowseView.js +0 -129
- package/js/Session.js +0 -107
- package/js/SessionControl.js +0 -26
- package/js/Unredacted.js +0 -82
- package/ts/CBIOBroadcastPickerView.d.ts +0 -8
- package/ts/CobrowseAccessibilityService.d.ts +0 -6
- package/ts/CobrowseIO.d.ts +0 -29
- package/ts/CobrowseView.d.ts +0 -20
- package/ts/Redacted.d.ts +0 -8
- package/ts/Session.d.ts +0 -58
- package/ts/SessionControl.d.ts +0 -9
- package/ts/Unredacted.d.ts +0 -16
- package/ts/index.d.ts +0 -7
- package/ts/useSession.d.ts +0 -3
package/android/build.gradle
CHANGED
|
@@ -79,6 +79,32 @@ public class CobrowseIOModule extends ReactContextBaseJavaModule {
|
|
|
79
79
|
CobrowseIO.instance().customData(customData.toHashMap());
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
@ReactMethod
|
|
83
|
+
public void capabilities(ReadableArray capabilities) {
|
|
84
|
+
String[] capabilitiesArray = new String[capabilities.size()];
|
|
85
|
+
for (int i = 0; i < capabilities.size(); i++) {
|
|
86
|
+
try {
|
|
87
|
+
capabilitiesArray[i] = capabilities.getString(i);
|
|
88
|
+
} catch (UnexpectedNativeTypeException e) {
|
|
89
|
+
Log.w("CobrowseIO", "Failed to process capabilities: " + e.getMessage());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
CobrowseIO.instance().capabilities(capabilitiesArray);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@ReactMethod
|
|
96
|
+
public void webviewRedactedViews(ReadableArray redactionSelectors) {
|
|
97
|
+
String[] redactionSelectorsArray = new String[redactionSelectors.size()];
|
|
98
|
+
for (int i = 0; i < redactionSelectors.size(); i++) {
|
|
99
|
+
try {
|
|
100
|
+
redactionSelectorsArray[i] = redactionSelectors.getString(i);
|
|
101
|
+
} catch (UnexpectedNativeTypeException e) {
|
|
102
|
+
Log.w("CobrowseIO", "Failed to process webviewRedactedViews: " + e.getMessage());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
CobrowseIO.instance().webviewRedactedViews(redactionSelectorsArray);
|
|
106
|
+
}
|
|
107
|
+
|
|
82
108
|
@ReactMethod
|
|
83
109
|
public void deviceToken(String token) {
|
|
84
110
|
if (getReactApplicationContext().getCurrentActivity() != null)
|
|
@@ -240,4 +266,14 @@ public class CobrowseIOModule extends ReactContextBaseJavaModule {
|
|
|
240
266
|
|
|
241
267
|
CobrowseIO.instance().setDelegate(delegatesWithSessionControls);
|
|
242
268
|
}
|
|
269
|
+
|
|
270
|
+
@ReactMethod
|
|
271
|
+
public void addListener(String eventName) {
|
|
272
|
+
// Keep: Required for RN built in Event Emitter Calls.
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@ReactMethod
|
|
276
|
+
public void removeListeners(Integer count) {
|
|
277
|
+
// Keep: Required for RN built in Event Emitter Calls.
|
|
278
|
+
}
|
|
243
279
|
}
|
|
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.homepage = package["homepage"]
|
|
12
12
|
s.source = { :git => 'https://github.com/cobrowseio/cobrowse-sdk-react-native.git' }
|
|
13
13
|
s.platform = :ios, '11.0'
|
|
14
|
-
s.dependency 'CobrowseIO/XCFramework', '2.
|
|
14
|
+
s.dependency 'CobrowseIO/XCFramework', '2.28.1'
|
|
15
15
|
s.dependency 'React'
|
|
16
16
|
s.source_files = 'ios/*.{h,m}'
|
|
17
17
|
end
|
package/ios/RCTCobrowseIO.m
CHANGED
|
@@ -161,6 +161,14 @@ RCT_EXPORT_METHOD(customData: (NSDictionary*) customData) {
|
|
|
161
161
|
CobrowseIO.instance.customData = customData;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
RCT_EXPORT_METHOD(capabilities: (NSArray*) capabilities) {
|
|
165
|
+
CobrowseIO.instance.capabilities = capabilities;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
RCT_EXPORT_METHOD(webviewRedactedViews: (NSArray*) redactionSelectors) {
|
|
169
|
+
CobrowseIO.instance.webviewRedactedViews = redactionSelectors;
|
|
170
|
+
}
|
|
171
|
+
|
|
164
172
|
RCT_EXPORT_METHOD(deviceToken: (NSString*) token) {
|
|
165
173
|
CobrowseIO.instance.device.token = [token dataUsingEncoding:NSUTF16StringEncoding];
|
|
166
174
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Platform, requireNativeComponent, type ViewProps } from 'react-native'
|
|
3
|
+
|
|
4
|
+
const CBIOBroadcastPickerViewNativeView = requireNativeComponent(
|
|
5
|
+
'CBIOBroadcastPickerView'
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
type CBIOBroadcastPickerViewProps = {
|
|
9
|
+
buttonColor: string
|
|
10
|
+
} & ViewProps
|
|
11
|
+
|
|
12
|
+
export default function CBIOBroadcastPickerView (props: CBIOBroadcastPickerViewProps): JSX.Element | null {
|
|
13
|
+
// this native component is only available on iOS
|
|
14
|
+
return Platform.OS === 'ios' ? <CBIOBroadcastPickerViewNativeView {...props} /> : null
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native'
|
|
2
|
+
const CobrowseIONative = NativeModules.CobrowseIO
|
|
3
|
+
|
|
4
|
+
export function showSetup (): void {
|
|
5
|
+
return CobrowseIONative.accessibilityServiceShowSetup()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isRunning (): boolean {
|
|
9
|
+
return CobrowseIONative.accessibilityServiceIsRunning()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
showSetup,
|
|
14
|
+
isRunning
|
|
15
|
+
}
|
package/js/CobrowseIO.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Platform, Alert, NativeEventEmitter, EmitterSubscription, NativeModules } from 'react-native'
|
|
2
|
+
import Session, { type CobrowseSessionEvents, type SessionLike } from './Session'
|
|
3
|
+
const CobrowseIONative = NativeModules.CobrowseIO
|
|
4
|
+
|
|
5
|
+
const emitter = new NativeEventEmitter(CobrowseIONative)
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
8
|
+
export default class CobrowseIO {
|
|
9
|
+
private static sessionRequestShown = false
|
|
10
|
+
private static remoteControlRequestShown = false
|
|
11
|
+
|
|
12
|
+
/** @deprecated */
|
|
13
|
+
static get SESSION_UPDATED (): 'session.updated' {
|
|
14
|
+
return 'session.updated' as const
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** @deprecated */
|
|
18
|
+
static get SESSION_ENDED (): 'session.ended' {
|
|
19
|
+
return 'session.ended' as const
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static handleSessionRequest (session: Session): void {
|
|
23
|
+
if (this.sessionRequestShown) return
|
|
24
|
+
this.sessionRequestShown = true
|
|
25
|
+
Alert.alert(
|
|
26
|
+
'Support Request',
|
|
27
|
+
'A support agent would like to use this app with you. Do you accept?',
|
|
28
|
+
[{
|
|
29
|
+
text: 'Reject',
|
|
30
|
+
onPress: () => {
|
|
31
|
+
this.sessionRequestShown = false
|
|
32
|
+
void session.end()
|
|
33
|
+
},
|
|
34
|
+
style: 'cancel'
|
|
35
|
+
}, {
|
|
36
|
+
text: 'Accept',
|
|
37
|
+
onPress: () => {
|
|
38
|
+
this.sessionRequestShown = false
|
|
39
|
+
void session.activate()
|
|
40
|
+
}
|
|
41
|
+
}], { cancelable: false })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static handleRemoteControlRequest (session: Session): void {
|
|
45
|
+
if (this.remoteControlRequestShown) return
|
|
46
|
+
this.remoteControlRequestShown = true
|
|
47
|
+
Alert.alert(
|
|
48
|
+
'Remote Control Request',
|
|
49
|
+
'A support agent would like to take remote control of this app. Do you accept?',
|
|
50
|
+
[{
|
|
51
|
+
text: 'Reject',
|
|
52
|
+
onPress: () => {
|
|
53
|
+
this.remoteControlRequestShown = false
|
|
54
|
+
void session.setRemoteControl('rejected')
|
|
55
|
+
},
|
|
56
|
+
style: 'cancel'
|
|
57
|
+
}, {
|
|
58
|
+
text: 'Accept',
|
|
59
|
+
onPress: () => {
|
|
60
|
+
this.remoteControlRequestShown = false
|
|
61
|
+
void session.setRemoteControl('on')
|
|
62
|
+
}
|
|
63
|
+
}], { cancelable: false })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static addListener (event: CobrowseSessionEvents | 'session.requested', cb: (session: Session) => void): EmitterSubscription {
|
|
67
|
+
return emitter.addListener(event, (session: SessionLike) => cb(new Session(session)))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static start (): void {
|
|
71
|
+
CobrowseIONative.start()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static stop (): void {
|
|
75
|
+
CobrowseIONative.stop()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line accessor-pairs
|
|
79
|
+
static set api (api: string) {
|
|
80
|
+
CobrowseIONative.api(api)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line accessor-pairs
|
|
84
|
+
static set license (license: string) {
|
|
85
|
+
CobrowseIONative.license(license)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// eslint-disable-next-line accessor-pairs
|
|
89
|
+
static set customData (customData: Record<string, unknown>) {
|
|
90
|
+
CobrowseIONative.customData(customData)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line accessor-pairs
|
|
94
|
+
static set capabilities (capabilities: Array<'drawing' | 'full_device' | 'keypress' | 'laser' | 'pointer'>) {
|
|
95
|
+
CobrowseIONative.capabilities(capabilities)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// eslint-disable-next-line accessor-pairs
|
|
99
|
+
static set webviewRedactedViews (redactionSelectors: string[]) {
|
|
100
|
+
CobrowseIONative.webviewRedactedViews(redactionSelectors)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// eslint-disable-next-line accessor-pairs
|
|
104
|
+
static set deviceToken (token: string) {
|
|
105
|
+
CobrowseIONative.deviceToken(token)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static currentSession (): Session | null {
|
|
109
|
+
return CobrowseIONative.currentSession().then((session: SessionLike | null) => (session != null) ? new Session(session) : null)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static createSession (): Session | null {
|
|
113
|
+
return CobrowseIONative.createSession().then((session: SessionLike | null) => (session != null) ? new Session(session) : null)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static getSession (codeOrId: string): Session | null {
|
|
117
|
+
return CobrowseIONative.getSession(codeOrId).then((session: SessionLike | null) => (session != null) ? new Session(session) : null)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @deprecated */
|
|
121
|
+
static activateSession (): Session | null {
|
|
122
|
+
return CobrowseIONative.activateSession().then((session: SessionLike | null) => (session != null) ? new Session(session) : null)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @deprecated */
|
|
126
|
+
static endSession (): void {
|
|
127
|
+
return CobrowseIONative.endSession()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static showSessionControls: Boolean | null = null
|
|
131
|
+
static handleFullDeviceRequest: null | ((s: Session) => void) = null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// the session.requested event is considered internal, it should
|
|
135
|
+
// not be used outside these bindings
|
|
136
|
+
CobrowseIO.addListener('session.requested', (session) => {
|
|
137
|
+
if (CobrowseIO.showSessionControls === false) {
|
|
138
|
+
CobrowseIONative.overwriteSessionIndicator()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (Platform.OS === 'ios' && (CobrowseIO.handleFullDeviceRequest != null)) {
|
|
142
|
+
CobrowseIONative.overwriteFullControlUI()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
CobrowseIO.handleSessionRequest(session)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
CobrowseIO.addListener('session.updated', (session) => {
|
|
149
|
+
if (session.isActive() && session.remote_control === 'requested') {
|
|
150
|
+
CobrowseIO.handleRemoteControlRequest(session)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (session.isActive() && session.full_device_state === 'requested') {
|
|
154
|
+
if (CobrowseIO.handleFullDeviceRequest != null) {
|
|
155
|
+
CobrowseIO.handleFullDeviceRequest(session)
|
|
156
|
+
} else if (Platform.OS === 'android') {
|
|
157
|
+
// accept the incoming connection by default
|
|
158
|
+
void session.setFullDevice('on')
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
ActivityIndicator,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
EmitterSubscription
|
|
9
|
+
} from 'react-native'
|
|
10
|
+
import CobrowseIO from './CobrowseIO'
|
|
11
|
+
import type Session from './Session'
|
|
12
|
+
|
|
13
|
+
const styles = StyleSheet.create({
|
|
14
|
+
container: {
|
|
15
|
+
flex: 1,
|
|
16
|
+
justifyContent: 'flex-end'
|
|
17
|
+
},
|
|
18
|
+
text: {
|
|
19
|
+
textAlign: 'center',
|
|
20
|
+
margin: 15,
|
|
21
|
+
fontSize: 15,
|
|
22
|
+
lineHeight: 20
|
|
23
|
+
},
|
|
24
|
+
code: {
|
|
25
|
+
fontSize: 29,
|
|
26
|
+
padding: 20,
|
|
27
|
+
fontWeight: 'bold',
|
|
28
|
+
textAlign: 'center'
|
|
29
|
+
},
|
|
30
|
+
button: {
|
|
31
|
+
color: 'rgb(0, 122, 255)',
|
|
32
|
+
fontSize: 18,
|
|
33
|
+
margin: 10
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
interface CobrowseViewProps {
|
|
38
|
+
// @deprecated
|
|
39
|
+
license?: string
|
|
40
|
+
onEnded?: () => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface CobrowseViewState {
|
|
44
|
+
error: Error | null
|
|
45
|
+
session: Session | null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default class CobrowseView extends Component<CobrowseViewProps, CobrowseViewState> {
|
|
49
|
+
private _updateListener: EmitterSubscription | null = null
|
|
50
|
+
private _endListener: EmitterSubscription | null = null
|
|
51
|
+
|
|
52
|
+
constructor (props: CobrowseViewProps) {
|
|
53
|
+
super(props)
|
|
54
|
+
this.state = { error: null, session: null }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async componentDidMount (): Promise<void> {
|
|
58
|
+
if (this.props.license != null) {
|
|
59
|
+
console.warn('Passing license to view is deprecated. Use CobrowseIO.license = "..." instead')
|
|
60
|
+
CobrowseIO.license = this.props.license
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const current = await CobrowseIO.currentSession()
|
|
65
|
+
if (current != null) this.setState({ session: current })
|
|
66
|
+
else {
|
|
67
|
+
const session = await CobrowseIO.createSession()
|
|
68
|
+
this.setState({ session })
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.setState({ error: error as Error })
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this._updateListener = CobrowseIO.addListener('session.updated', (session) => {
|
|
75
|
+
this.setState({ session })
|
|
76
|
+
})
|
|
77
|
+
this._endListener = CobrowseIO.addListener('session.ended', () => {
|
|
78
|
+
if (this.props.onEnded != null) this.props.onEnded()
|
|
79
|
+
this.setState({ session: null })
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
componentWillUnmount (): void {
|
|
84
|
+
if (this._updateListener != null) this._updateListener.remove()
|
|
85
|
+
if (this._endListener != null) this._endListener.remove()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async endSession (): Promise<void> {
|
|
89
|
+
try {
|
|
90
|
+
const { session } = this.state
|
|
91
|
+
|
|
92
|
+
if (session == null) {
|
|
93
|
+
throw new Error('No session to end')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await session.end()
|
|
97
|
+
this.setState({ session: null })
|
|
98
|
+
} catch (error) {
|
|
99
|
+
this.setState({ error: error as Error })
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
renderError (error: Error): JSX.Element {
|
|
104
|
+
return (
|
|
105
|
+
<Text style={[styles.text]}>{error.message}</Text>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
renderCode (): JSX.Element {
|
|
110
|
+
let code = this.state.session?.code
|
|
111
|
+
if (code != null) code = code.substr(0, 3) + '-' + code.substr(3)
|
|
112
|
+
return (
|
|
113
|
+
<View>
|
|
114
|
+
<Text style={[styles.code, { opacity: code != null ? 1 : 0.2 }]}>{code ?? '000-000'}</Text>
|
|
115
|
+
<Text style={[styles.text]}>Provide this code to your support agent to begin screen sharing.</Text>
|
|
116
|
+
<ActivityIndicator />
|
|
117
|
+
</View>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
renderManageSession (): JSX.Element {
|
|
122
|
+
return (
|
|
123
|
+
<View>
|
|
124
|
+
<Text style={[styles.text]}>You're sharing screens from this app with a support agent.</Text>
|
|
125
|
+
<TouchableOpacity onPress={() => this.endSession}>
|
|
126
|
+
<Text style={[styles.text, styles.button]}>End Session</Text>
|
|
127
|
+
</TouchableOpacity>
|
|
128
|
+
</View>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
renderContent (): JSX.Element {
|
|
133
|
+
const { error, session } = this.state
|
|
134
|
+
if (error != null) {
|
|
135
|
+
return this.renderError(error)
|
|
136
|
+
} else if ((session == null) || (session.state === 'pending' || session.state === 'authorizing')) {
|
|
137
|
+
return this.renderCode()
|
|
138
|
+
} else {
|
|
139
|
+
return this.renderManageSession()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
render (): JSX.Element {
|
|
144
|
+
return (
|
|
145
|
+
<View style={styles.container}>
|
|
146
|
+
{this.renderContent()}
|
|
147
|
+
</View>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { requireNativeComponent } from 'react-native'
|
|
2
|
+
import { requireNativeComponent, type ViewProps } from 'react-native'
|
|
3
|
+
|
|
3
4
|
const CBIOCobrowseRedacted = requireNativeComponent('CBIOCobrowseRedacted')
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
export default function Redacted (props: ViewProps): JSX.Element {
|
|
6
7
|
return <CBIOCobrowseRedacted {...props}>{props.children}</CBIOCobrowseRedacted>
|
|
7
8
|
}
|
package/js/Session.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { EmitterSubscription, NativeEventEmitter, NativeModules } from 'react-native'
|
|
2
|
+
const CobrowseIONative = NativeModules.CobrowseIO
|
|
3
|
+
|
|
4
|
+
const emitter = new NativeEventEmitter(CobrowseIONative)
|
|
5
|
+
|
|
6
|
+
export type SessionEvent = 'updated' | 'ended'
|
|
7
|
+
export type CobrowseSessionEvents = `session.${SessionEvent}`
|
|
8
|
+
export type SessionState = 'active' | 'authorizing' | 'ended' | 'pending'
|
|
9
|
+
export type RemoteControlState = 'on' | 'requested' | 'rejected' | 'off'
|
|
10
|
+
export type FullDeviceState = 'on' | 'requested' | 'rejected' | 'off'
|
|
11
|
+
|
|
12
|
+
export interface Agent {
|
|
13
|
+
id: string
|
|
14
|
+
name: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SessionLike {
|
|
18
|
+
id: string
|
|
19
|
+
code: string
|
|
20
|
+
state: SessionState
|
|
21
|
+
full_device: boolean
|
|
22
|
+
remote_control: RemoteControlState
|
|
23
|
+
agent: Agent
|
|
24
|
+
full_device_state: FullDeviceState
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default class Session {
|
|
28
|
+
constructor (private session: SessionLike | Record<string, never> = {}) {
|
|
29
|
+
this._listen()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private _update (session: SessionLike, onMatch?: () => void): void {
|
|
33
|
+
if (this.session.id === undefined || (session != null && this.session.id === session.id)) {
|
|
34
|
+
this.session = session
|
|
35
|
+
if (typeof onMatch === 'function') onMatch()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_listen (): void {
|
|
40
|
+
const updates = emitter.addListener('session.updated', (session: SessionLike) => this._update(session))
|
|
41
|
+
const ended = emitter.addListener('session.ended', (session: SessionLike) => {
|
|
42
|
+
this._update(session, () => {
|
|
43
|
+
updates.remove()
|
|
44
|
+
ended.remove()
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
addListener (eventType: SessionEvent, listener: (session: Session) => void): EmitterSubscription {
|
|
50
|
+
let mappedEventsType: CobrowseSessionEvents
|
|
51
|
+
// TODO: this class should extend EventEmitter and forward
|
|
52
|
+
// on the non-namespaced versions of these events
|
|
53
|
+
switch (eventType) {
|
|
54
|
+
case 'ended':
|
|
55
|
+
mappedEventsType = 'session.updated'
|
|
56
|
+
break
|
|
57
|
+
case 'updated':
|
|
58
|
+
mappedEventsType = 'session.ended'
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return emitter.addListener(mappedEventsType ?? eventType, (session: SessionLike) => {
|
|
63
|
+
this._update(session, () => {
|
|
64
|
+
listener(this)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get id (): string | null {
|
|
70
|
+
return this.session.id
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get code (): string | null {
|
|
74
|
+
return this.session.code
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get state (): SessionState | null {
|
|
78
|
+
return this.session.state
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get full_device (): boolean {
|
|
82
|
+
return this.session.full_device ?? false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get full_device_state (): FullDeviceState {
|
|
86
|
+
return this.session.full_device_state ?? 'off'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get remote_control (): RemoteControlState | null {
|
|
90
|
+
return this.session.remote_control
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get agent (): Agent | null {
|
|
94
|
+
return this.session.agent
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async activate (): Promise<void> {
|
|
98
|
+
return CobrowseIONative.activateSession().then(() => {})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async end (): Promise<void> {
|
|
102
|
+
return CobrowseIONative.endSession()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
isActive (): boolean {
|
|
106
|
+
return this.session.state === 'active'
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
isAuthorizing (): boolean {
|
|
110
|
+
return this.session.state === 'authorizing'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
isPending (): boolean {
|
|
114
|
+
return this.session.state === 'pending'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isEnded (): boolean {
|
|
118
|
+
return this.session.state === 'ended'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async setFullDevice (state: boolean | FullDeviceState): Promise<void> {
|
|
122
|
+
return CobrowseIONative.updateSession({ full_device: state })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async setRemoteControl (state: RemoteControlState): Promise<void> {
|
|
126
|
+
return CobrowseIONative.updateSession({ remote_control: state })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import CobrowseIO from './CobrowseIO'
|
|
3
|
+
import type { EmitterSubscription } from 'react-native'
|
|
4
|
+
import type Session from './Session'
|
|
5
|
+
|
|
6
|
+
interface SessionControlProps {
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface SessionControlState {
|
|
11
|
+
session: Session | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class SessionControl extends Component<SessionControlProps, SessionControlState> {
|
|
15
|
+
private _updateListener: EmitterSubscription | null = null
|
|
16
|
+
|
|
17
|
+
constructor (props: SessionControlProps) {
|
|
18
|
+
super(props)
|
|
19
|
+
this.state = { session: null }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async componentDidMount (): Promise<void> {
|
|
23
|
+
this.setState({ session: await CobrowseIO.currentSession() })
|
|
24
|
+
this._updateListener = CobrowseIO.addListener(CobrowseIO.SESSION_UPDATED, (session) => {
|
|
25
|
+
this.setState({ session })
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
componentWillUnmount (): void {
|
|
30
|
+
if (this._updateListener != null) this._updateListener.remove()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render (): React.ReactNode | null {
|
|
34
|
+
const { session } = this.state
|
|
35
|
+
if (session?.isActive() === true) return this.props.children
|
|
36
|
+
else return null
|
|
37
|
+
}
|
|
38
|
+
}
|