react-native-acoustic-connect-beta 19.0.13 → 19.0.15
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/CHANGELOG.md +6 -0
- package/ConnectConfig.example.json +7 -7
- package/README.md +123 -1
- package/android/build.gradle +64 -0
- package/android/config.gradle +123 -12
- package/android/src/main/assets/ConnectAdvancedConfig.json +1 -1
- package/android/src/main/assets/ConnectBasicConfig.properties +1 -1
- package/android/src/main/java/com/acousticconnectrn/HybridAcousticConnectRN.kt +164 -18
- package/cli/index.mjs +0 -0
- package/ios/AcousticConnectRNConfig.json +153 -8
- package/ios/ConnectRNParsing.swift +152 -0
- package/ios/HybridAcousticConnectRN.swift +56 -15
- package/lib/commonjs/index.js +3 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/react-native-acoustic-connect.nitro.d.ts +139 -10
- package/lib/typescript/src/specs/react-native-acoustic-connect.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridAcousticConnectRNSpec.cpp +5 -9
- package/nitrogen/generated/android/c++/JHybridAcousticConnectRNSpec.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/acousticconnectrn/HybridAcousticConnectRNSpec.kt +2 -1
- package/nitrogen/generated/ios/AcousticConnectRN-Swift-Cxx-Umbrella.hpp +1 -0
- package/nitrogen/generated/ios/c++/HybridAcousticConnectRNSpecSwift.hpp +2 -1
- package/nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec_cxx.swift +2 -25
- package/nitrogen/generated/shared/c++/HybridAcousticConnectRNSpec.hpp +2 -1
- package/package.json +2 -1
- package/src/index.ts +5 -0
- package/src/specs/react-native-acoustic-connect.nitro.ts +141 -10
- package/Examples/SampleUI/ConnectConfig.json +0 -180
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Created on 5/9/25.
|
|
11
11
|
//
|
|
12
12
|
|
|
13
|
-
import { type HybridObject } from 'react-native-nitro-modules'
|
|
13
|
+
import { type AnyMap, type HybridObject } from 'react-native-nitro-modules'
|
|
14
14
|
|
|
15
15
|
// Define a named type for the anonymous object
|
|
16
16
|
export type KeyValueObject = {
|
|
@@ -18,6 +18,32 @@ export type KeyValueObject = {
|
|
|
18
18
|
[key: string]: unknown;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Payload accepted by {@link AcousticConnectRN.logSignal} — any JSON-shaped
|
|
23
|
+
* object. Nested objects and arrays are allowed, so this is a superset of a
|
|
24
|
+
* flat `Record<string, string | number | boolean>`.
|
|
25
|
+
*
|
|
26
|
+
* You do not need this type to call `logSignal`: an object literal is enough.
|
|
27
|
+
* It exists for the times you want to name the payload — a helper parameter, a
|
|
28
|
+
* shared constant — and is re-exported from the package root so you never have
|
|
29
|
+
* to reach into `react-native-nitro-modules` for it:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* import AcousticConnectRN, { type SignalValues } from 'react-native-acoustic-connect-beta'
|
|
33
|
+
*
|
|
34
|
+
* const payload: SignalValues = { signalContent: { signalType: 'pageview' } }
|
|
35
|
+
* AcousticConnectRN.logSignal(payload, 1)
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* Aliases nitro's `AnyMap`, which is the only way to express arbitrary JSON in
|
|
40
|
+
* a Nitro spec — a hand-rolled recursive type is rejected by nitrogen, which
|
|
41
|
+
* tries to generate a struct for it. Nitrogen resolves the alias, so the
|
|
42
|
+
* generated native bindings are identical either way; the indirection exists
|
|
43
|
+
* purely to keep a third-party type name off our public API.
|
|
44
|
+
*/
|
|
45
|
+
export type SignalValues = AnyMap
|
|
46
|
+
|
|
21
47
|
export type ConnectMonitoringLevelType = 'Ignore' | 'CellularAndWiFi' | 'WiFi'
|
|
22
48
|
|
|
23
49
|
/**
|
|
@@ -123,8 +149,75 @@ export interface AcousticConnectRN extends HybridObject<{ ios: 'swift', android:
|
|
|
123
149
|
getBooleanConfigItemForKey(theDefault: boolean, key: string, moduleName: string): boolean
|
|
124
150
|
getStringItemForKey(theDefault: string, key: string, moduleName: string): string | null | undefined
|
|
125
151
|
getNumberItemForKey(theDefault: number, key: string, moduleName: string): number
|
|
152
|
+
/**
|
|
153
|
+
* Logs a named custom event with a flat set of key/value pairs.
|
|
154
|
+
*
|
|
155
|
+
* `values` is deliberately **flat** (scalars only), unlike
|
|
156
|
+
* {@link logSignal}. The Android SDK's custom-event path is typed
|
|
157
|
+
* `HashMap<String, String>` end to end (`Connect.logCustomEvent` →
|
|
158
|
+
* `Tealeaf.logCustomEvent`), so there is no native route for nested
|
|
159
|
+
* JSON. Widening this signature would compile but silently drop the
|
|
160
|
+
* nesting on Android while preserving it on iOS — a cross-platform
|
|
161
|
+
* divergence worse than the restriction. Use {@link logSignal} when the
|
|
162
|
+
* payload needs structure.
|
|
163
|
+
*
|
|
164
|
+
* @param eventName Event name; appears in the posted JSON.
|
|
165
|
+
* @param values Flat key/value pairs to attach to the event.
|
|
166
|
+
* @param level Monitoring level for this event.
|
|
167
|
+
* @returns `true` if the event was queued.
|
|
168
|
+
*/
|
|
126
169
|
logCustomEvent(eventName: string, values: Record<string, string | number | boolean>, level: number): boolean
|
|
127
|
-
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Logs a signal payload.
|
|
173
|
+
*
|
|
174
|
+
* `values` accepts **arbitrary JSON** — nested objects and arrays, not
|
|
175
|
+
* just scalars — because both native SDKs embed the payload verbatim:
|
|
176
|
+
* iOS `CTSignalMessage` gates only on `NSJSONSerialization
|
|
177
|
+
* isValidJSONObject:`, and Android `Connect.logSignal` takes an untyped
|
|
178
|
+
* `HashMap<String, Any?>`. The bridge converts the map to each
|
|
179
|
+
* platform's JSON representation without flattening or reshaping it.
|
|
180
|
+
*
|
|
181
|
+
* Scalar-only callers are unaffected: {@link SignalValues} is a superset
|
|
182
|
+
* of `Record<string, string | number | boolean>`.
|
|
183
|
+
*
|
|
184
|
+
* @param values Signal payload. Objects, arrays, strings, numbers,
|
|
185
|
+
* booleans and `null` are all carried through.
|
|
186
|
+
* @param level Monitoring level for this signal.
|
|
187
|
+
* @returns `true` if the signal was queued.
|
|
188
|
+
*
|
|
189
|
+
* @remarks
|
|
190
|
+
* **Android limitation — top-level numbers.** The Android SDK's
|
|
191
|
+
* `JsonUtil.getHashValues` serialises only `String`, `Boolean`,
|
|
192
|
+
* `JSONObject`, `JSONArray` and `byte[]` at the top level of the signal
|
|
193
|
+
* map; a top-level numeric value is dropped. Numbers nested *inside* an
|
|
194
|
+
* object or array are unaffected, because the bridge builds those
|
|
195
|
+
* `JSONObject`/`JSONArray` values itself. iOS carries top-level numbers
|
|
196
|
+
* normally. Send numbers inside a nested object when cross-platform
|
|
197
|
+
* parity matters. This is a pre-existing SDK-side gap, not a
|
|
198
|
+
* restriction of this bridge.
|
|
199
|
+
*
|
|
200
|
+
* @example Nested payload
|
|
201
|
+
* ```ts
|
|
202
|
+
* import AcousticConnectRN from 'react-native-acoustic-connect-beta'
|
|
203
|
+
*
|
|
204
|
+
* AcousticConnectRN.logSignal(
|
|
205
|
+
* {
|
|
206
|
+
* signalContent: {
|
|
207
|
+
* signalType: 'pageview',
|
|
208
|
+
* url: 'https://app.example.com/dashboard',
|
|
209
|
+
* pageCategory: 'dashboard',
|
|
210
|
+
* },
|
|
211
|
+
* audience: [
|
|
212
|
+
* { name: 'Account Name', value: 'Acme Corp' },
|
|
213
|
+
* { name: 'Account ID', value: '4815162342' },
|
|
214
|
+
* ],
|
|
215
|
+
* },
|
|
216
|
+
* 1
|
|
217
|
+
* )
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
logSignal(values: SignalValues, level: number): boolean
|
|
128
221
|
logExceptionEvent(message: string, stackInfo: string, unhandled: boolean): boolean
|
|
129
222
|
logLocation(): boolean
|
|
130
223
|
logLocationWithLatitudeLongitude(latitude: number, longitude: number, level: number): boolean
|
|
@@ -138,6 +231,13 @@ export interface AcousticConnectRN extends HybridObject<{ ios: 'swift', android:
|
|
|
138
231
|
logDialogShowEvent(dialogId: string, dialogTitle: string, dialogType: string): boolean
|
|
139
232
|
logDialogDismissEvent(dialogId: string, dismissReason: string): boolean
|
|
140
233
|
logDialogButtonClickEvent(dialogId: string, buttonText: string, buttonIndex: number): boolean
|
|
234
|
+
/**
|
|
235
|
+
* Logs a custom event against a tracked dialog.
|
|
236
|
+
*
|
|
237
|
+
* `values` is flat for the same reason as {@link logCustomEvent} — this
|
|
238
|
+
* routes to the native custom-event API, whose Android path carries string
|
|
239
|
+
* values only.
|
|
240
|
+
*/
|
|
141
241
|
logDialogCustomEvent(dialogId: string, eventName: string, values: Record<string, string | number | boolean>): boolean
|
|
142
242
|
|
|
143
243
|
/**
|
|
@@ -161,23 +261,54 @@ export interface AcousticConnectRN extends HybridObject<{ ios: 'swift', android:
|
|
|
161
261
|
* `'loggedIn'` when omitted (identity logging typically marks a sign-in),
|
|
162
262
|
* overriding the native SDKs' own `'pageView'` default.
|
|
163
263
|
* @param additionalParameters Optional extra key/value pairs merged into the
|
|
164
|
-
* signal payload. Only when omitted (`undefined`) does the bridge supply
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* API expects it).
|
|
264
|
+
* signal payload. Only when omitted (`undefined`) does the bridge supply a
|
|
265
|
+
* default, and that default follows the resolved `signalType` — see
|
|
266
|
+
* **Required method attribute** below. An explicitly-provided map is used
|
|
267
|
+
* as-is, so passing an empty `{}` sends no extra parameters (the default is
|
|
268
|
+
* not merged in). A `'url'` entry is honoured uniformly on both platforms:
|
|
269
|
+
* on Android it is routed to the SDK's explicit `url` parameter, on iOS it
|
|
270
|
+
* rides inside the parameter map (where the native API expects it).
|
|
171
271
|
* @returns A promise resolving to `true` if the identity signal was
|
|
172
272
|
* dispatched, `false` otherwise (including the blank-identifier case).
|
|
173
273
|
* Never rejects.
|
|
174
274
|
*
|
|
175
|
-
*
|
|
275
|
+
* ### Required method attribute
|
|
276
|
+
*
|
|
277
|
+
* Identity signals must carry a *method* attribute, and the key Connect
|
|
278
|
+
* requires depends on the signal type:
|
|
279
|
+
*
|
|
280
|
+
* | `signalType` | Required attribute |
|
|
281
|
+
* | -------------------- | -------------------- |
|
|
282
|
+
* | `'loggedIn'` | `loginMethod` |
|
|
283
|
+
* | `'accountRegistered'`| `registrationMethod` |
|
|
284
|
+
*
|
|
285
|
+
* Send the wrong key and the signal fails schema validation and is
|
|
286
|
+
* discarded server-side — while this promise still resolves `true`, because
|
|
287
|
+
* the native SDKs report only that the signal was queued. If you pass
|
|
288
|
+
* `additionalParameters` explicitly, you own supplying the right key.
|
|
289
|
+
*
|
|
290
|
+
* @example Sign-in — `loginMethod` is required
|
|
176
291
|
* ```ts
|
|
177
292
|
* import AcousticConnectRN from 'react-native-acoustic-connect-beta'
|
|
178
293
|
*
|
|
294
|
+
* await AcousticConnectRN.logIdentity('Email', 'user@example.com', 'loggedIn', {
|
|
295
|
+
* loginMethod: 'sso',
|
|
296
|
+
* })
|
|
297
|
+
*
|
|
298
|
+
* // Omitting both arguments is equivalent to the above with
|
|
299
|
+
* // `{ loginMethod: 'email' }` — the bridge defaults to a `loggedIn` signal.
|
|
179
300
|
* await AcousticConnectRN.logIdentity('Email', 'user@example.com')
|
|
180
301
|
* ```
|
|
302
|
+
*
|
|
303
|
+
* @example Registration — `registrationMethod` is required
|
|
304
|
+
* ```ts
|
|
305
|
+
* await AcousticConnectRN.logIdentity(
|
|
306
|
+
* 'Email',
|
|
307
|
+
* 'user@example.com',
|
|
308
|
+
* 'accountRegistered',
|
|
309
|
+
* { registrationMethod: 'email' }
|
|
310
|
+
* )
|
|
311
|
+
* ```
|
|
181
312
|
*/
|
|
182
313
|
logIdentity(
|
|
183
314
|
identifierName: string,
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Connect": {
|
|
3
|
-
"AndroidVersion": "10.4.48",
|
|
4
|
-
"AppKey": "fa47722a7fef4bcd8677fd8d6d113a0d",
|
|
5
|
-
"KillSwitchUrl": "https://lib-us-2.brilliantcollector.com/collector/switch/b6c3709b7a4c479bb4b5a9fb8fec324c",
|
|
6
|
-
"PostMessageUrl": "https://lib-us-2.brilliantcollector.com/collector/collectorPost",
|
|
7
|
-
"iOSVersion": "",
|
|
8
|
-
"layoutConfigAndroid": {
|
|
9
|
-
"AppendMapIds": {
|
|
10
|
-
"[w,9290],[v,0]": {
|
|
11
|
-
"mid": "ASimpleUIView"
|
|
12
|
-
},
|
|
13
|
-
"idxPathValue": {
|
|
14
|
-
"mid": "giveAdditionalId2"
|
|
15
|
-
},
|
|
16
|
-
"tag2999999": {
|
|
17
|
-
"mid": "giveAdditionalId1"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"AutoLayout": {
|
|
21
|
-
"ExampleMaskingPage": {
|
|
22
|
-
"CaptureLayoutDelay": 0,
|
|
23
|
-
"CaptureLayoutOn": 0,
|
|
24
|
-
"CaptureScreenVisits": false,
|
|
25
|
-
"CaptureScreenshotOn": 0,
|
|
26
|
-
"CaptureUserEvents": false,
|
|
27
|
-
"DisplayName": "",
|
|
28
|
-
"Masking": {
|
|
29
|
-
"HasCustomMask": true,
|
|
30
|
-
"HasMasking": true,
|
|
31
|
-
"MaskAccessibilityIdList": [
|
|
32
|
-
|
|
33
|
-
],
|
|
34
|
-
"MaskAccessibilityLabelList": [
|
|
35
|
-
|
|
36
|
-
],
|
|
37
|
-
"MaskIdList": [
|
|
38
|
-
"^9[0-9][0-9][0-9]$",
|
|
39
|
-
"^\\[wvv,0\\],\\[dddv,0\\],\\[v,0\\],\\[v,0\\],\\[v,0\\],\\[b,0\\](.)*$"
|
|
40
|
-
],
|
|
41
|
-
"MaskValueList": [
|
|
42
|
-
"^4[0-9]{12}(?:[0-9]{3})?$",
|
|
43
|
-
"^3[47][0-9]{13}$",
|
|
44
|
-
"^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$",
|
|
45
|
-
"^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$"
|
|
46
|
-
],
|
|
47
|
-
"Sensitive": {
|
|
48
|
-
"capitalCaseAlphabet": "X",
|
|
49
|
-
"number": "9",
|
|
50
|
-
"smallCaseAlphabet": "x",
|
|
51
|
-
"symbol": "#"
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"NumberOfWebViews": 0,
|
|
55
|
-
"ScreenChange": false,
|
|
56
|
-
"ScreenShot": false
|
|
57
|
-
},
|
|
58
|
-
"GlobalScreenSettings": {
|
|
59
|
-
"CaptureLayoutDelay": 1,
|
|
60
|
-
"CaptureLayoutOn": 2,
|
|
61
|
-
"CaptureScreenVisits": false,
|
|
62
|
-
"CaptureScreenshotOn": 2,
|
|
63
|
-
"CaptureUserEvents": true,
|
|
64
|
-
"DisplayName": "",
|
|
65
|
-
"Masking": {
|
|
66
|
-
"HasCustomMask": true,
|
|
67
|
-
"HasMasking": true,
|
|
68
|
-
"MaskAccessibilityIdList": [
|
|
69
|
-
|
|
70
|
-
],
|
|
71
|
-
"MaskAccessibilityLabelList": [
|
|
72
|
-
|
|
73
|
-
],
|
|
74
|
-
"MaskIdList": [
|
|
75
|
-
|
|
76
|
-
],
|
|
77
|
-
"MaskValueList": [
|
|
78
|
-
|
|
79
|
-
],
|
|
80
|
-
"Sensitive": {
|
|
81
|
-
"capitalCaseAlphabet": "X",
|
|
82
|
-
"number": "9",
|
|
83
|
-
"smallCaseAlphabet": "x",
|
|
84
|
-
"symbol": "#"
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
"NumberOfWebViews": 0,
|
|
88
|
-
"ScreenChange": true,
|
|
89
|
-
"ScreenShot": true
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
"layoutConfigIos": {
|
|
94
|
-
"AppendMapIds": {
|
|
95
|
-
"[w,9290],[v,0]": {
|
|
96
|
-
"mid": "ASimpleUIView"
|
|
97
|
-
},
|
|
98
|
-
"idxPathValue": {
|
|
99
|
-
"mid": "giveAdditionalId2"
|
|
100
|
-
},
|
|
101
|
-
"tag2999999": {
|
|
102
|
-
"mid": "giveAdditionalId1"
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
"AutoLayout": {
|
|
106
|
-
"ExampleMaskingPage": {
|
|
107
|
-
"CaptureLayoutDelay": 0,
|
|
108
|
-
"CaptureLayoutOn": 0,
|
|
109
|
-
"CaptureScreenVisits": false,
|
|
110
|
-
"CaptureScreenshotOn": 0,
|
|
111
|
-
"CaptureUserEvents": false,
|
|
112
|
-
"DisplayName": "",
|
|
113
|
-
"Masking": {
|
|
114
|
-
"HasCustomMask": true,
|
|
115
|
-
"HasMasking": true,
|
|
116
|
-
"MaskAccessibilityIdList": [
|
|
117
|
-
|
|
118
|
-
],
|
|
119
|
-
"MaskAccessibilityLabelList": [
|
|
120
|
-
|
|
121
|
-
],
|
|
122
|
-
"MaskIdList": [
|
|
123
|
-
"^9[0-9][0-9][0-9]$",
|
|
124
|
-
"^\\[wvv,0\\],\\[dddv,0\\],\\[v,0\\],\\[v,0\\],\\[v,0\\],\\[b,0\\](.)*$"
|
|
125
|
-
],
|
|
126
|
-
"MaskValueList": [
|
|
127
|
-
"^4[0-9]{12}(?:[0-9]{3})?$",
|
|
128
|
-
"^3[47][0-9]{13}$",
|
|
129
|
-
"^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$",
|
|
130
|
-
"^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$"
|
|
131
|
-
],
|
|
132
|
-
"Sensitive": {
|
|
133
|
-
"capitalCaseAlphabet": "X",
|
|
134
|
-
"number": "9",
|
|
135
|
-
"smallCaseAlphabet": "x",
|
|
136
|
-
"symbol": "#"
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
"NumberOfWebViews": 0,
|
|
140
|
-
"ScreenChange": false,
|
|
141
|
-
"ScreenShot": false
|
|
142
|
-
},
|
|
143
|
-
"GlobalScreenSettings": {
|
|
144
|
-
"CaptureLayoutDelay": 1,
|
|
145
|
-
"CaptureLayoutOn": 2,
|
|
146
|
-
"CaptureScreenVisits": false,
|
|
147
|
-
"CaptureScreenshotOn": 2,
|
|
148
|
-
"CaptureUserEvents": true,
|
|
149
|
-
"DisplayName": "",
|
|
150
|
-
"Masking": {
|
|
151
|
-
"HasCustomMask": true,
|
|
152
|
-
"HasMasking": true,
|
|
153
|
-
"MaskAccessibilityIdList": [
|
|
154
|
-
|
|
155
|
-
],
|
|
156
|
-
"MaskAccessibilityLabelList": [
|
|
157
|
-
|
|
158
|
-
],
|
|
159
|
-
"MaskIdList": [
|
|
160
|
-
|
|
161
|
-
],
|
|
162
|
-
"MaskValueList": [
|
|
163
|
-
|
|
164
|
-
],
|
|
165
|
-
"Sensitive": {
|
|
166
|
-
"capitalCaseAlphabet": "X",
|
|
167
|
-
"number": "9",
|
|
168
|
-
"smallCaseAlphabet": "x",
|
|
169
|
-
"symbol": "#"
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
"NumberOfWebViews": 0,
|
|
173
|
-
"ScreenChange": true,
|
|
174
|
-
"ScreenShot": true
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
"useRelease": false
|
|
179
|
-
}
|
|
180
|
-
}
|