Skip to content

Commit ac7e386

Browse files
author
Ivan Kudinov
committed
CCS-112452 aurora platform support
1 parent 8a6fd9d commit ac7e386

9 files changed

Lines changed: 304 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SmartApp bridge library
22

33
This library provides a universal interface for exchanging events with an express client.
4-
Andriod, iOS and Web clients supported.
4+
Andriod, iOS, Aurora and Web clients supported.
55

66
All types can be found [here](https://smartapp.ccsteam.xyz/smartapp-bridge/).
77

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@expressms/smartapp-bridge",
3-
"version": "1.4.5",
3+
"version": "1.5.0-alpha.1",
44
"description": "SmartApp bridge library",
55
"main": "build/main/index.js",
66
"typings": "build/main/index.d.ts",

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PLATFORM } from './lib/constants'
22
import getPlatform from './lib/platformDetector'
33
import AndroidBridge from './lib/platforms/android'
4+
import AuroraBridge from './lib/platforms/aurora'
45
import IosBridge from './lib/platforms/ios'
56
import WebBridge from './lib/platforms/web'
67
import { Bridge } from './types/bridgeInterface'
@@ -15,6 +16,8 @@ const getBridge = (): Bridge | null => {
1516
switch (platform) {
1617
case PLATFORM.ANDROID:
1718
return new AndroidBridge()
19+
case PLATFORM.AURORA:
20+
return new AuroraBridge()
1821
case PLATFORM.IOS:
1922
return new IosBridge()
2023
case PLATFORM.WEB:

src/lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export enum PLATFORM {
22
WEB = 'web',
33
IOS = 'ios',
44
ANDROID = 'android',
5+
AURORA = 'aurora',
56
UNKNOWN = 'unknown',
67
}
78

src/lib/platformDetector.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const detectPlatformByUserAgent = (): PLATFORM => {
2121
)
2222
return PLATFORM.IOS
2323

24+
if (/aurora/i.test(navigator.userAgent)) {
25+
return PLATFORM.AURORA
26+
}
27+
2428
return PLATFORM.WEB
2529
}
2630

@@ -30,7 +34,7 @@ const detectPlatformByUserAgent = (): PLATFORM => {
3034
* ```typescript
3135
* const platform = getPlatform();
3236
*
33-
* // => 'web' | 'ios' | 'android'
37+
* // => 'web' | 'ios' | 'android' | 'aurora'
3438
* ```
3539
*/
3640
const getPlatform = (): PLATFORM => {

src/lib/platforms/aurora.ts

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import { v4 as uuid } from 'uuid'
2+
3+
import {
4+
Bridge,
5+
BridgeSendBotEventParams,
6+
BridgeSendClientEventParams,
7+
BridgeSendEventParams,
8+
EventEmitterCallback,
9+
} from '../../types'
10+
import { camelCaseToSnakeCase, snakeCaseToCamelCase } from '../case'
11+
import { EVENT_TYPE, HANDLER, RESPONSE_TIMEOUT, SYNC_RESPONSE_TIMEOUT, WEB_COMMAND_TYPE_RPC } from '../constants'
12+
import ExtendedEventEmitter from '../eventEmitter'
13+
import Logger from '../logger'
14+
15+
class AuroraBridge extends Logger implements Bridge {
16+
private readonly eventEmitter: ExtendedEventEmitter
17+
isRenameParamsEnabledForBotx: boolean
18+
19+
constructor() {
20+
super()
21+
22+
this.eventEmitter = new ExtendedEventEmitter()
23+
this.isRenameParamsEnabledForBotx = true
24+
window.handleAuroraEvent = this.handleAuroraEvent.bind(this)
25+
}
26+
27+
private handleAuroraEvent({
28+
ref,
29+
data,
30+
files,
31+
}: {
32+
readonly ref: string
33+
readonly data: {
34+
readonly type: string
35+
}
36+
readonly files: any
37+
}): void {
38+
this.logRecvEvent({ ref, data, files })
39+
40+
const { type, ...payload } = data
41+
42+
const emitterType = ref || EVENT_TYPE.RECEIVE
43+
44+
const eventFiles = this.isRenameParamsEnabledForBotx ? files?.map((file: any) => snakeCaseToCamelCase(file)) : files
45+
46+
const event = {
47+
ref,
48+
type,
49+
payload: this.isRenameParamsEnabledForBotx ? snakeCaseToCamelCase(payload) : payload,
50+
files: eventFiles,
51+
}
52+
53+
this.eventEmitter.emit(emitterType, event)
54+
}
55+
56+
/**
57+
* Set callback function to handle events without **ref**
58+
* (notifications for example).
59+
*
60+
* ```js
61+
* bridge.onRecieve(({ type, handler, payload }) => {
62+
* // Handle event data
63+
* console.log('event', type, handler, payload)
64+
* })
65+
* ```
66+
* @param callback - Callback function.
67+
*/
68+
onReceive(callback: EventEmitterCallback) {
69+
this.eventEmitter.on(EVENT_TYPE.RECEIVE, callback)
70+
}
71+
72+
private sendEvent({
73+
handler,
74+
method,
75+
params,
76+
files,
77+
timeout = RESPONSE_TIMEOUT,
78+
guaranteed_delivery_required = false,
79+
sync_request = false,
80+
sync_request_timeout = SYNC_RESPONSE_TIMEOUT,
81+
hide_send_event_data = false,
82+
hide_recv_event_data = false,
83+
}: BridgeSendEventParams) {
84+
const ref = uuid() // UUID to detect express response.
85+
const isRenameParamsEnabled = handler === HANDLER.BOTX ? this.isRenameParamsEnabledForBotx : true
86+
const eventProps = {
87+
ref,
88+
type: WEB_COMMAND_TYPE_RPC,
89+
method,
90+
handler,
91+
payload: isRenameParamsEnabled ? camelCaseToSnakeCase(params) : params,
92+
guaranteed_delivery_required,
93+
sync_request,
94+
sync_request_timeout,
95+
hide_send_event_data,
96+
hide_recv_event_data,
97+
}
98+
99+
const eventFiles = isRenameParamsEnabled ? files?.map((file: any) => camelCaseToSnakeCase(file)) : files
100+
101+
const event = files ? { ...eventProps, files: eventFiles } : eventProps
102+
103+
this.logSendEvent(event)
104+
105+
const customEvent = new CustomEvent('framescript:action', { detail: event })
106+
107+
if (window.sendAsyncMessage) {
108+
window.sendAsyncMessage('webview:action', event)
109+
} else {
110+
document.dispatchEvent(customEvent)
111+
}
112+
113+
return this.eventEmitter.onceWithTimeout(ref, timeout)
114+
}
115+
116+
/**
117+
* Send event and wait response from express client.
118+
*
119+
* ```js
120+
* bridge
121+
* .sendBotEvent(
122+
* {
123+
* method: 'get_weather',
124+
* params: {
125+
* city: 'Moscow',
126+
* },
127+
* files: []
128+
* }
129+
* )
130+
* .then(data => {
131+
* // Handle response
132+
* console.log('response', data)
133+
* })
134+
* ```
135+
*/
136+
sendBotEvent({
137+
method,
138+
params,
139+
files,
140+
timeout = RESPONSE_TIMEOUT,
141+
guaranteed_delivery_required,
142+
sync_request,
143+
sync_request_timeout,
144+
hide_send_event_data,
145+
hide_recv_event_data,
146+
}: BridgeSendBotEventParams) {
147+
return this.sendEvent({
148+
handler: HANDLER.BOTX,
149+
method,
150+
params,
151+
files,
152+
timeout,
153+
guaranteed_delivery_required,
154+
sync_request,
155+
sync_request_timeout,
156+
hide_send_event_data,
157+
hide_recv_event_data,
158+
})
159+
}
160+
161+
/**
162+
* Send event and wait response from express client.
163+
*
164+
* ```js
165+
* bridge
166+
* .sendClientEvent(
167+
* {
168+
* type: 'get_weather',
169+
* handler: 'express',
170+
* payload: {
171+
* city: 'Moscow',
172+
* },
173+
* }
174+
* )
175+
* .then(data => {
176+
* // Handle response
177+
* console.log('response', data)
178+
* })
179+
* ```
180+
*/
181+
sendClientEvent({
182+
method,
183+
params,
184+
timeout = RESPONSE_TIMEOUT,
185+
hide_send_event_data,
186+
hide_recv_event_data,
187+
}: BridgeSendClientEventParams) {
188+
return this.sendEvent({
189+
handler: HANDLER.EXPRESS,
190+
method,
191+
params,
192+
timeout,
193+
hide_send_event_data,
194+
hide_recv_event_data,
195+
})
196+
}
197+
198+
/**
199+
* Enabling renaming event params from camelCase to snake_case and vice versa
200+
* ```js
201+
* bridge
202+
* .enableRenameParams()
203+
* ```
204+
*/
205+
enableRenameParams() {
206+
this.isRenameParamsEnabledForBotx = true
207+
console.log('Bridge ~ Enabled renaming event params from camelCase to snake_case and vice versa')
208+
}
209+
210+
/**
211+
* Enabling renaming event params from camelCase to snake_case and vice versa
212+
* ```js
213+
* bridge
214+
* .disableRenameParams()
215+
* ```
216+
*/
217+
disableRenameParams() {
218+
this.isRenameParamsEnabledForBotx = false
219+
console.log('Bridge ~ Disabled renaming event params from camelCase to snake_case and vice versa')
220+
}
221+
222+
/**
223+
* Write log to client
224+
* @param data Any data to log
225+
*/
226+
log(data: string | object): void {
227+
let value: typeof data = ''
228+
if (typeof data === 'string') {
229+
value = data
230+
} else if (typeof data === 'object') {
231+
value = JSON.stringify(data, null, 2)
232+
} else return
233+
234+
const event = new CustomEvent('framescript:action', { detail: { 'SmartApp Log': value } })
235+
document.dispatchEvent(event)
236+
}
237+
}
238+
239+
export default AuroraBridge

src/types/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ declare global {
1717
}
1818
}
1919
}
20+
21+
// Aurora interface
22+
handleAuroraEvent: Function
23+
sendAsyncMessage?: (type: string, event: object) => void
2024
}
2125
}

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const LIB_VERSION = "1.4.4";
1+
export const LIB_VERSION = "1.5.0";

test/aurora.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>SmartApp bridge testing</title>
8+
<style>
9+
* {
10+
font-family: Arial, Helvetica, sans-serif;
11+
}
12+
</style>
13+
</head>
14+
15+
<body>
16+
<h1>Test bridge for Aurora</h1>
17+
<button onclick="sendMessageToClient()">Send message to client</button>
18+
<textarea style="width: 100%; height: 80vh; margin-top: 10px;" id="log"></textarea>
19+
<script>
20+
function log(...data) {
21+
var value = data.map(d => typeof d === 'object' && JSON.stringify(d) || `${d}`).join(", ");
22+
document.getElementById("log").value += `${value}\r\n`;
23+
}
24+
25+
function send(topic) {
26+
var customEvent = new CustomEvent("framescript:action",
27+
{detail: { topic: topic}});
28+
document.dispatchEvent(customEvent);
29+
}
30+
31+
window.addEventListener("DOMContentLoaded", function (event) {
32+
log('originalTarget', event.originalTarget);
33+
log('originalTarget name =',
34+
event.originalTarget &&
35+
event.originalTarget.__proto__ &&
36+
event.originalTarget.__proto__.constructor &&
37+
event.originalTarget.__proto__.constructor.name
38+
);
39+
});
40+
41+
// Отправка события
42+
function sendMessageToClient() {
43+
send('hello world')
44+
log('Event sent');
45+
}
46+
</script>
47+
</body>
48+
49+
</html>

0 commit comments

Comments
 (0)