@octopus-community/react-native 1.0.8 → 1.9.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/OctopusReactNativeSdk.podspec +1 -1
- package/README.md +40 -35
- package/android/build.gradle +2 -0
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +2 -1
- package/android/src/main/AndroidManifestNew.xml +2 -1
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusActivity.kt +56 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusContent.kt +396 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventEmitter.kt +22 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventSerializer.kt +339 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactModule.kt +326 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/{OctopusReactNativeSdkPackage.kt → OctopusReactPackage.kt} +3 -3
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +22 -9
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSSOAuthenticator.kt +5 -15
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIController.kt +17 -2
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIViewManager.kt +63 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/ProfileFieldMapper.kt +2 -2
- package/ios/OctopusEventManager.swift +27 -0
- package/ios/OctopusEventSerializer.swift +271 -0
- package/ios/OctopusReactNativeSdk.mm +26 -1
- package/ios/OctopusReactNativeSdk.swift +216 -2
- package/ios/OctopusSSOAuthenticator.swift +1 -5
- package/ios/OctopusUIManager.swift +83 -0
- package/ios/OctopusUIViewManager.m +7 -0
- package/ios/OctopusUIViewManager.swift +37 -0
- package/lib/module/OctopusUIView.js +39 -0
- package/lib/module/OctopusUIView.js.map +1 -0
- package/lib/module/addHasAccessToCommunityListener.js +33 -0
- package/lib/module/addHasAccessToCommunityListener.js.map +1 -0
- package/lib/module/addNavigateToUrlListener.js +41 -0
- package/lib/module/addNavigateToUrlListener.js.map +1 -0
- package/lib/module/addNotSeenNotificationsCountListener.js +30 -0
- package/lib/module/addNotSeenNotificationsCountListener.js.map +1 -0
- package/lib/module/addSDKEventListener.js +48 -0
- package/lib/module/addSDKEventListener.js.map +1 -0
- package/lib/module/connectUser.js +24 -3
- package/lib/module/connectUser.js.map +1 -1
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/initialize.js +9 -12
- package/lib/module/initialize.js.map +1 -1
- package/lib/module/openUI.js +23 -2
- package/lib/module/openUI.js.map +1 -1
- package/lib/module/overrideCommunityAccess.js +36 -0
- package/lib/module/overrideCommunityAccess.js.map +1 -0
- package/lib/module/overrideDefaultLocale.js +75 -0
- package/lib/module/overrideDefaultLocale.js.map +1 -0
- package/lib/module/trackCommunityAccess.js +33 -0
- package/lib/module/trackCommunityAccess.js.map +1 -0
- package/lib/module/trackCustomEvent.js +36 -0
- package/lib/module/trackCustomEvent.js.map +1 -0
- package/lib/module/types/sdkEvents.js +2 -0
- package/lib/module/types/sdkEvents.js.map +1 -0
- package/lib/module/types/urlOpeningStrategy.js +23 -0
- package/lib/module/types/urlOpeningStrategy.js.map +1 -0
- package/lib/module/updateNotSeenNotificationsCount.js +33 -0
- package/lib/module/updateNotSeenNotificationsCount.js.map +1 -0
- package/lib/typescript/src/OctopusUIView.d.ts +32 -0
- package/lib/typescript/src/OctopusUIView.d.ts.map +1 -0
- package/lib/typescript/src/addHasAccessToCommunityListener.d.ts +27 -0
- package/lib/typescript/src/addHasAccessToCommunityListener.d.ts.map +1 -0
- package/lib/typescript/src/addNavigateToUrlListener.d.ts +31 -0
- package/lib/typescript/src/addNavigateToUrlListener.d.ts.map +1 -0
- package/lib/typescript/src/addNotSeenNotificationsCountListener.d.ts +24 -0
- package/lib/typescript/src/addNotSeenNotificationsCountListener.d.ts.map +1 -0
- package/lib/typescript/src/addSDKEventListener.d.ts +43 -0
- package/lib/typescript/src/addSDKEventListener.d.ts.map +1 -0
- package/lib/typescript/src/connectUser.d.ts +24 -8
- package/lib/typescript/src/connectUser.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +13 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/initialize.d.ts +9 -12
- package/lib/typescript/src/initialize.d.ts.map +1 -1
- package/lib/typescript/src/openUI.d.ts +28 -1
- package/lib/typescript/src/openUI.d.ts.map +1 -1
- package/lib/typescript/src/overrideCommunityAccess.d.ts +30 -0
- package/lib/typescript/src/overrideCommunityAccess.d.ts.map +1 -0
- package/lib/typescript/src/overrideDefaultLocale.d.ts +37 -0
- package/lib/typescript/src/overrideDefaultLocale.d.ts.map +1 -0
- package/lib/typescript/src/trackCommunityAccess.d.ts +27 -0
- package/lib/typescript/src/trackCommunityAccess.d.ts.map +1 -0
- package/lib/typescript/src/trackCustomEvent.d.ts +30 -0
- package/lib/typescript/src/trackCustomEvent.d.ts.map +1 -0
- package/lib/typescript/src/types/sdkEvents.d.ts +222 -0
- package/lib/typescript/src/types/sdkEvents.d.ts.map +1 -0
- package/lib/typescript/src/types/urlOpeningStrategy.d.ts +20 -0
- package/lib/typescript/src/types/urlOpeningStrategy.d.ts.map +1 -0
- package/lib/typescript/src/updateNotSeenNotificationsCount.d.ts +27 -0
- package/lib/typescript/src/updateNotSeenNotificationsCount.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/OctopusUIView.tsx +57 -0
- package/src/addHasAccessToCommunityListener.ts +38 -0
- package/src/addNavigateToUrlListener.ts +54 -0
- package/src/addNotSeenNotificationsCountListener.ts +35 -0
- package/src/addSDKEventListener.ts +49 -0
- package/src/connectUser.ts +24 -8
- package/src/index.ts +13 -0
- package/src/initialize.ts +9 -12
- package/src/openUI.ts +32 -2
- package/src/overrideCommunityAccess.ts +33 -0
- package/src/overrideDefaultLocale.ts +88 -0
- package/src/trackCommunityAccess.ts +30 -0
- package/src/trackCustomEvent.ts +36 -0
- package/src/types/sdkEvents.ts +315 -0
- package/src/types/urlOpeningStrategy.ts +20 -0
- package/src/updateNotSeenNotificationsCount.ts +30 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +0 -155
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +0 -434
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventSerializer.kt
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
package com.octopuscommunity.octopusreactnativesdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.octopuscommunity.sdk.domain.model.Gamification
|
|
6
|
+
import com.octopuscommunity.sdk.domain.model.Moderation
|
|
7
|
+
import com.octopuscommunity.sdk.domain.model.OctopusEvent
|
|
8
|
+
import com.octopuscommunity.sdk.domain.model.OctopusItem
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Serializes OctopusEvent objects to WritableMap for React Native bridge.
|
|
12
|
+
*/
|
|
13
|
+
object OctopusEventSerializer {
|
|
14
|
+
|
|
15
|
+
fun serializeEvent(event: OctopusEvent): WritableMap? {
|
|
16
|
+
val map = Arguments.createMap()
|
|
17
|
+
|
|
18
|
+
return when (event) {
|
|
19
|
+
is OctopusEvent.PostCreated -> {
|
|
20
|
+
val contentList = event.content.map { content ->
|
|
21
|
+
when (content) {
|
|
22
|
+
OctopusEvent.PostCreated.Content.TEXT -> "text"
|
|
23
|
+
OctopusEvent.PostCreated.Content.IMAGE -> "image"
|
|
24
|
+
OctopusEvent.PostCreated.Content.POLL -> "poll"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
map.putString("type", "postCreated")
|
|
28
|
+
map.putString("postId", event.postId)
|
|
29
|
+
val contentArray = Arguments.createArray()
|
|
30
|
+
contentList.forEach { contentArray.pushString(it) }
|
|
31
|
+
map.putArray("content", contentArray)
|
|
32
|
+
map.putString("topicId", event.topicId)
|
|
33
|
+
map.putInt("textLength", event.textLength)
|
|
34
|
+
map
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
is OctopusEvent.CommentCreated -> {
|
|
38
|
+
map.putString("type", "commentCreated")
|
|
39
|
+
map.putString("commentId", event.commentId)
|
|
40
|
+
map.putString("postId", event.postId)
|
|
41
|
+
map.putInt("textLength", event.textLength)
|
|
42
|
+
map
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
is OctopusEvent.ReplyCreated -> {
|
|
46
|
+
map.putString("type", "replyCreated")
|
|
47
|
+
map.putString("replyId", event.replyId)
|
|
48
|
+
map.putString("commentId", event.commentId)
|
|
49
|
+
map.putInt("textLength", event.textLength)
|
|
50
|
+
map
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
is OctopusEvent.PostDeleted -> {
|
|
54
|
+
map.putString("type", "contentDeleted")
|
|
55
|
+
map.putString("contentId", event.contentId)
|
|
56
|
+
map.putString("contentKind", "post")
|
|
57
|
+
map
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
is OctopusEvent.CommentDeleted -> {
|
|
61
|
+
map.putString("type", "contentDeleted")
|
|
62
|
+
map.putString("contentId", event.contentId)
|
|
63
|
+
map.putString("contentKind", "comment")
|
|
64
|
+
map
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
is OctopusEvent.ReplyDeleted -> {
|
|
68
|
+
map.putString("type", "contentDeleted")
|
|
69
|
+
map.putString("contentId", event.contentId)
|
|
70
|
+
map.putString("contentKind", "reply")
|
|
71
|
+
map
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
is OctopusEvent.ReactionModified -> {
|
|
75
|
+
map.putString("type", "reactionModified")
|
|
76
|
+
map.putString("contentId", event.contentId)
|
|
77
|
+
map.putString("contentKind", serializeContentKind(event.contentKind))
|
|
78
|
+
event.previousReaction?.let {
|
|
79
|
+
map.putString("previousReaction", serializeReactionKind(it))
|
|
80
|
+
}
|
|
81
|
+
event.newReaction?.let {
|
|
82
|
+
map.putString("newReaction", serializeReactionKind(it))
|
|
83
|
+
}
|
|
84
|
+
map
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
is OctopusEvent.PollVote -> {
|
|
88
|
+
map.putString("type", "pollVoted")
|
|
89
|
+
map.putString("contentId", event.contentId)
|
|
90
|
+
map.putString("optionId", event.optionId)
|
|
91
|
+
map
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
is OctopusEvent.ContentReported -> {
|
|
95
|
+
map.putString("type", "contentReported")
|
|
96
|
+
map.putString("contentId", event.contentId)
|
|
97
|
+
val reasonsArray = Arguments.createArray()
|
|
98
|
+
event.reasons.forEach { reason ->
|
|
99
|
+
reasonsArray.pushString(serializeReportReason(reason))
|
|
100
|
+
}
|
|
101
|
+
map.putArray("reasons", reasonsArray)
|
|
102
|
+
map
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
is OctopusEvent.ProfileReported -> {
|
|
106
|
+
map.putString("type", "profileReported")
|
|
107
|
+
map.putString("profileId", event.profileId)
|
|
108
|
+
val reasonsArray = Arguments.createArray()
|
|
109
|
+
event.reasons.forEach { reason ->
|
|
110
|
+
reasonsArray.pushString(serializeReportReason(reason))
|
|
111
|
+
}
|
|
112
|
+
map.putArray("reasons", reasonsArray)
|
|
113
|
+
map
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
is OctopusEvent.GamificationPointsGained -> {
|
|
117
|
+
map.putString("type", "gamificationPointsGained")
|
|
118
|
+
map.putInt("points", event.points)
|
|
119
|
+
map.putString("action", serializeGamificationAction(event.action))
|
|
120
|
+
map
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
is OctopusEvent.GamificationPointsRemoved -> {
|
|
124
|
+
map.putString("type", "gamificationPointsRemoved")
|
|
125
|
+
map.putInt("points", event.points)
|
|
126
|
+
map.putString("action", serializeGamificationPointsRemovedAction(event.action))
|
|
127
|
+
map
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
is OctopusEvent.ScreenDisplayed -> {
|
|
131
|
+
map.putString("type", "screenDisplayed")
|
|
132
|
+
val screenMap = serializeScreen(event)
|
|
133
|
+
map.putMap("screen", screenMap)
|
|
134
|
+
map
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
is OctopusEvent.NotificationClicked -> {
|
|
138
|
+
map.putString("type", "notificationClicked")
|
|
139
|
+
map.putString("notificationId", event.notificationId)
|
|
140
|
+
event.contentId?.let {
|
|
141
|
+
map.putString("contentId", it)
|
|
142
|
+
}
|
|
143
|
+
map
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
is OctopusEvent.PostClicked -> {
|
|
147
|
+
map.putString("type", "postClicked")
|
|
148
|
+
map.putString("postId", event.postId)
|
|
149
|
+
map.putString("source", when (event.source) {
|
|
150
|
+
OctopusEvent.PostClicked.Source.FEED -> "feed"
|
|
151
|
+
OctopusEvent.PostClicked.Source.PROFILE -> "profile"
|
|
152
|
+
})
|
|
153
|
+
map
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
is OctopusEvent.TranslationButtonClicked -> {
|
|
157
|
+
map.putString("type", "translationButtonClicked")
|
|
158
|
+
map.putString("contentId", event.contentId)
|
|
159
|
+
map.putBoolean("viewTranslated", event.viewTranslated)
|
|
160
|
+
map.putString("contentKind", serializeContentKind(event.contentKind))
|
|
161
|
+
map
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
is OctopusEvent.CommentButtonClicked -> {
|
|
165
|
+
map.putString("type", "commentButtonClicked")
|
|
166
|
+
map.putString("postId", event.postId)
|
|
167
|
+
map
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
is OctopusEvent.ReplyButtonClicked -> {
|
|
171
|
+
map.putString("type", "replyButtonClicked")
|
|
172
|
+
map.putString("commentId", event.commentId)
|
|
173
|
+
map
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
is OctopusEvent.SeeRepliesButtonClicked -> {
|
|
177
|
+
map.putString("type", "seeRepliesButtonClicked")
|
|
178
|
+
map.putString("commentId", event.commentId)
|
|
179
|
+
map
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
is OctopusEvent.ProfileModified -> {
|
|
183
|
+
val prev = event.previousProfile
|
|
184
|
+
val new = event.newProfile
|
|
185
|
+
val nicknameUpdated = prev?.nickname != new.nickname
|
|
186
|
+
val bioUpdated = prev?.bio != new.bio
|
|
187
|
+
val pictureUpdated = prev?.picture != new.picture
|
|
188
|
+
|
|
189
|
+
map.putString("type", "profileModified")
|
|
190
|
+
map.putBoolean("nicknameUpdated", nicknameUpdated)
|
|
191
|
+
map.putBoolean("bioUpdated", bioUpdated)
|
|
192
|
+
if (bioUpdated) {
|
|
193
|
+
new.bio?.length?.let { map.putInt("bioLength", it) }
|
|
194
|
+
}
|
|
195
|
+
map.putBoolean("pictureUpdated", pictureUpdated)
|
|
196
|
+
if (pictureUpdated) {
|
|
197
|
+
map.putBoolean("hasPicture", new.picture != null)
|
|
198
|
+
}
|
|
199
|
+
map
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
is OctopusEvent.SessionStarted -> {
|
|
203
|
+
map.putString("type", "sessionStarted")
|
|
204
|
+
map.putString("sessionId", event.sessionId)
|
|
205
|
+
map
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
is OctopusEvent.SessionStopped -> {
|
|
209
|
+
map.putString("type", "sessionStopped")
|
|
210
|
+
map.putString("sessionId", event.sessionId)
|
|
211
|
+
map
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
else -> null
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private fun serializeContentKind(kind: OctopusItem.ContentKind): String {
|
|
219
|
+
return when (kind) {
|
|
220
|
+
OctopusItem.ContentKind.POST -> "post"
|
|
221
|
+
OctopusItem.ContentKind.COMMENT -> "comment"
|
|
222
|
+
OctopusItem.ContentKind.REPLY -> "reply"
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private fun serializeReactionKind(kind: OctopusItem.Reaction.Kind): String {
|
|
227
|
+
return when (kind) {
|
|
228
|
+
is OctopusItem.Reaction.Kind.Heart -> "heart"
|
|
229
|
+
is OctopusItem.Reaction.Kind.Joy -> "joy"
|
|
230
|
+
is OctopusItem.Reaction.Kind.MouthOpen -> "mouthOpen"
|
|
231
|
+
is OctopusItem.Reaction.Kind.Clap -> "clap"
|
|
232
|
+
is OctopusItem.Reaction.Kind.Cry -> "cry"
|
|
233
|
+
is OctopusItem.Reaction.Kind.Rage -> "rage"
|
|
234
|
+
is OctopusItem.Reaction.Kind.Unknown -> "unknown"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private fun serializeReportReason(reason: Moderation.ReportReason): String {
|
|
239
|
+
return when (reason) {
|
|
240
|
+
is Moderation.ReportReason.HateSpeechOrDiscriminatoryContent -> "hateSpeech"
|
|
241
|
+
is Moderation.ReportReason.ExplicitOrInappropriateContent -> "explicit"
|
|
242
|
+
is Moderation.ReportReason.ViolenceAndTerrorism -> "violence"
|
|
243
|
+
is Moderation.ReportReason.SpamAndScams -> "spam"
|
|
244
|
+
is Moderation.ReportReason.SuicideAndSelfHarm -> "suicide"
|
|
245
|
+
is Moderation.ReportReason.FakeProfilesAndImpersonation -> "fakeProfile"
|
|
246
|
+
is Moderation.ReportReason.ChildExploitationOrAbuse -> "childExploitation"
|
|
247
|
+
is Moderation.ReportReason.IntellectualPropertyViolation -> "intellectualProperty"
|
|
248
|
+
is Moderation.ReportReason.Other -> "other"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private fun serializeGamificationAction(action: Gamification.Action): String {
|
|
253
|
+
return when (action) {
|
|
254
|
+
Gamification.Action.POST -> "post"
|
|
255
|
+
Gamification.Action.COMMENT -> "comment"
|
|
256
|
+
Gamification.Action.REPLY -> "reply"
|
|
257
|
+
Gamification.Action.REACTION -> "reaction"
|
|
258
|
+
Gamification.Action.VOTE -> "vote"
|
|
259
|
+
Gamification.Action.POST_COMMENTED -> "postCommented"
|
|
260
|
+
Gamification.Action.PROFILE_COMPLETED -> "profileCompleted"
|
|
261
|
+
Gamification.Action.DAILY_SESSION -> "dailySession"
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private fun serializeGamificationPointsRemovedAction(action: Gamification.Action): String {
|
|
266
|
+
// In Android SDK, GamificationPointsRemoved uses the same enum values as GamificationPointsGained,
|
|
267
|
+
// but the context (removed vs gained) determines the meaning.
|
|
268
|
+
// Map the action values to their "removed" equivalents
|
|
269
|
+
return when (action) {
|
|
270
|
+
Gamification.Action.POST -> "postDeleted"
|
|
271
|
+
Gamification.Action.COMMENT -> "commentDeleted"
|
|
272
|
+
Gamification.Action.REPLY -> "replyDeleted"
|
|
273
|
+
Gamification.Action.REACTION -> "reactionDeleted"
|
|
274
|
+
// For other actions, use the regular serialization (shouldn't happen for removed events)
|
|
275
|
+
else -> serializeGamificationAction(action)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private fun serializeScreen(event: OctopusEvent.ScreenDisplayed): WritableMap {
|
|
280
|
+
val screenMap = Arguments.createMap()
|
|
281
|
+
|
|
282
|
+
when (event) {
|
|
283
|
+
is OctopusEvent.ScreenDisplayed.PostsFeed -> {
|
|
284
|
+
screenMap.putString("type", "postsFeed")
|
|
285
|
+
screenMap.putString("feedId", event.feedId)
|
|
286
|
+
event.relatedTopicId?.let {
|
|
287
|
+
screenMap.putString("relatedTopicId", it)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
is OctopusEvent.ScreenDisplayed.PostDetail -> {
|
|
291
|
+
screenMap.putString("type", "postDetail")
|
|
292
|
+
screenMap.putString("postId", event.postId)
|
|
293
|
+
}
|
|
294
|
+
is OctopusEvent.ScreenDisplayed.CommentDetail -> {
|
|
295
|
+
screenMap.putString("type", "commentDetail")
|
|
296
|
+
screenMap.putString("commentId", event.commentId)
|
|
297
|
+
}
|
|
298
|
+
is OctopusEvent.ScreenDisplayed.CreatePost -> {
|
|
299
|
+
screenMap.putString("type", "createPost")
|
|
300
|
+
}
|
|
301
|
+
is OctopusEvent.ScreenDisplayed.Profile -> {
|
|
302
|
+
screenMap.putString("type", "profile")
|
|
303
|
+
}
|
|
304
|
+
is OctopusEvent.ScreenDisplayed.OtherUserProfile -> {
|
|
305
|
+
screenMap.putString("type", "otherUserProfile")
|
|
306
|
+
screenMap.putString("profileId", event.profileId)
|
|
307
|
+
}
|
|
308
|
+
is OctopusEvent.ScreenDisplayed.EditProfile -> {
|
|
309
|
+
screenMap.putString("type", "editProfile")
|
|
310
|
+
}
|
|
311
|
+
is OctopusEvent.ScreenDisplayed.ReportContent -> {
|
|
312
|
+
screenMap.putString("type", "reportContent")
|
|
313
|
+
}
|
|
314
|
+
is OctopusEvent.ScreenDisplayed.ReportProfile -> {
|
|
315
|
+
screenMap.putString("type", "reportProfile")
|
|
316
|
+
}
|
|
317
|
+
is OctopusEvent.ScreenDisplayed.ValidateNickname -> {
|
|
318
|
+
screenMap.putString("type", "validateNickname")
|
|
319
|
+
}
|
|
320
|
+
is OctopusEvent.ScreenDisplayed.SettingsList -> {
|
|
321
|
+
screenMap.putString("type", "settingsList")
|
|
322
|
+
}
|
|
323
|
+
is OctopusEvent.ScreenDisplayed.SettingsAccount -> {
|
|
324
|
+
screenMap.putString("type", "settingsAccount")
|
|
325
|
+
}
|
|
326
|
+
is OctopusEvent.ScreenDisplayed.SettingsAbout -> {
|
|
327
|
+
screenMap.putString("type", "settingsAbout")
|
|
328
|
+
}
|
|
329
|
+
is OctopusEvent.ScreenDisplayed.ReportExplanation -> {
|
|
330
|
+
screenMap.putString("type", "reportExplanation")
|
|
331
|
+
}
|
|
332
|
+
is OctopusEvent.ScreenDisplayed.DeleteAccount -> {
|
|
333
|
+
screenMap.putString("type", "deleteAccount")
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return screenMap
|
|
338
|
+
}
|
|
339
|
+
}
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactModule.kt
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
package com.octopuscommunity.octopusreactnativesdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
5
|
+
import com.facebook.react.bridge.ReactMethod
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.ReadableMap
|
|
8
|
+
import com.facebook.react.bridge.ReadableType
|
|
9
|
+
import com.octopuscommunity.sdk.OctopusSDK
|
|
10
|
+
import com.octopuscommunity.sdk.domain.model.TrackerEvent
|
|
11
|
+
import kotlinx.coroutines.CoroutineScope
|
|
12
|
+
import kotlinx.coroutines.Dispatchers
|
|
13
|
+
import kotlinx.coroutines.Job
|
|
14
|
+
import kotlinx.coroutines.SupervisorJob
|
|
15
|
+
import kotlinx.coroutines.launch
|
|
16
|
+
|
|
17
|
+
class OctopusReactModule(reactContext: ReactApplicationContext) :
|
|
18
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
19
|
+
|
|
20
|
+
private val sdkInitializer = OctopusSDKInitializer()
|
|
21
|
+
private val eventEmitter = OctopusEventEmitter(reactContext)
|
|
22
|
+
private val uiController = OctopusUIController(reactContext)
|
|
23
|
+
private val ssoAuthenticator = OctopusSSOAuthenticator(reactContext, eventEmitter)
|
|
24
|
+
private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
25
|
+
private var notSeenNotificationsJob: Job? = null
|
|
26
|
+
private var hasAccessToCommunityJob: Job? = null
|
|
27
|
+
private var eventsJob: Job? = null
|
|
28
|
+
|
|
29
|
+
override fun getName(): String = NAME
|
|
30
|
+
|
|
31
|
+
@ReactMethod
|
|
32
|
+
fun initialize(options: ReadableMap, promise: Promise) {
|
|
33
|
+
val success = sdkInitializer.initialize(reactApplicationContext, options, promise)
|
|
34
|
+
// Start observing reactive events after SDK initialization succeeds
|
|
35
|
+
if (success) {
|
|
36
|
+
startObservingReactiveEvents()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@ReactMethod
|
|
41
|
+
fun openUI(options: ReadableMap?, promise: Promise) {
|
|
42
|
+
uiController.openUI(options, promise)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@ReactMethod
|
|
46
|
+
fun handleUrlStrategy(url: String, strategy: String) {
|
|
47
|
+
if (strategy == "handledByOctopus") {
|
|
48
|
+
uiController.openUrlInBrowser(url)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@ReactMethod
|
|
53
|
+
fun closeUI(promise: Promise) {
|
|
54
|
+
uiController.closeUI(promise)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@ReactMethod
|
|
58
|
+
fun connectUser(params: ReadableMap, promise: Promise) {
|
|
59
|
+
ssoAuthenticator.connectUser(params, promise)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@ReactMethod
|
|
63
|
+
fun disconnectUser(promise: Promise) {
|
|
64
|
+
ssoAuthenticator.disconnectUser(promise)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@ReactMethod
|
|
68
|
+
fun completeUserTokenRequest(requestId: String, token: String, promise: Promise) {
|
|
69
|
+
ssoAuthenticator.completeTokenRequest(requestId, token)
|
|
70
|
+
promise.resolve(null)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@ReactMethod
|
|
74
|
+
fun cancelUserTokenRequest(requestId: String, promise: Promise) {
|
|
75
|
+
ssoAuthenticator.cancelTokenRequest(requestId)
|
|
76
|
+
promise.resolve(null)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@ReactMethod
|
|
80
|
+
fun addListener(eventName: String) {
|
|
81
|
+
eventEmitter.addListener(eventName)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@ReactMethod
|
|
85
|
+
fun removeListeners(count: Int) {
|
|
86
|
+
eventEmitter.removeListeners(count)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@ReactMethod
|
|
90
|
+
fun updateColorScheme(colorScheme: String?, promise: Promise) {
|
|
91
|
+
// Update the theme config with the new color scheme
|
|
92
|
+
val currentThemeConfig = OctopusThemeManager.getThemeConfig()
|
|
93
|
+
if (currentThemeConfig != null) {
|
|
94
|
+
val updatedThemeConfig = currentThemeConfig.copy(colorScheme = colorScheme)
|
|
95
|
+
OctopusThemeManager.setThemeConfig(updatedThemeConfig)
|
|
96
|
+
}
|
|
97
|
+
promise.resolve(null)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@ReactMethod
|
|
101
|
+
fun updateTheme(themeOptions: ReadableMap, promise: Promise) {
|
|
102
|
+
try {
|
|
103
|
+
// Parse theme using the standard initializer
|
|
104
|
+
val themeConfig = sdkInitializer.parseThemeConfig(themeOptions)
|
|
105
|
+
OctopusThemeManager.setThemeConfig(themeConfig)
|
|
106
|
+
promise.resolve(null)
|
|
107
|
+
} catch (e: Exception) {
|
|
108
|
+
promise.reject("UPDATE_THEME_ERROR", "Failed to update theme", e)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@ReactMethod
|
|
113
|
+
fun updateNotSeenNotificationsCount(promise: Promise) {
|
|
114
|
+
coroutineScope.launch {
|
|
115
|
+
try {
|
|
116
|
+
OctopusSDK.updateNotSeenNotificationsCount()
|
|
117
|
+
promise.resolve(null)
|
|
118
|
+
} catch (e: Exception) {
|
|
119
|
+
promise.reject("UPDATE_ERROR", e.message ?: "Failed to update notification count", e)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@ReactMethod
|
|
125
|
+
fun trackCustomEvent(name: String, properties: ReadableMap?, promise: Promise) {
|
|
126
|
+
val trimmedName = name.trim()
|
|
127
|
+
if (trimmedName.isEmpty()) {
|
|
128
|
+
promise.reject("INVALID_ARGS", "name is required and must be non-empty", null)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
val propsMap = readableMapToStringMap(properties)
|
|
132
|
+
coroutineScope.launch {
|
|
133
|
+
try {
|
|
134
|
+
OctopusSDK.track(
|
|
135
|
+
TrackerEvent.Custom(
|
|
136
|
+
name = trimmedName,
|
|
137
|
+
properties = propsMap.mapValues { TrackerEvent.Custom.Property(it.value) }
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
promise.resolve(null)
|
|
141
|
+
} catch (e: Exception) {
|
|
142
|
+
promise.reject("TRACK_ERROR", e.message ?: "Failed to track custom event", e)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@ReactMethod
|
|
148
|
+
fun overrideDefaultLocale(
|
|
149
|
+
languageCode: String?,
|
|
150
|
+
countryCode: String?,
|
|
151
|
+
promise: Promise
|
|
152
|
+
) {
|
|
153
|
+
coroutineScope.launch {
|
|
154
|
+
try {
|
|
155
|
+
val locale = when {
|
|
156
|
+
languageCode != null && countryCode != null ->
|
|
157
|
+
java.util.Locale(languageCode, countryCode)
|
|
158
|
+
languageCode != null -> java.util.Locale(languageCode)
|
|
159
|
+
else -> null
|
|
160
|
+
}
|
|
161
|
+
OctopusSDK.overrideDefaultLocale(locale)
|
|
162
|
+
promise.resolve(null)
|
|
163
|
+
} catch (e: Exception) {
|
|
164
|
+
promise.reject(
|
|
165
|
+
"LOCALE_ERROR",
|
|
166
|
+
e.message ?: "Failed to override default locale",
|
|
167
|
+
e
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@ReactMethod
|
|
174
|
+
fun overrideCommunityAccess(hasAccess: Boolean, promise: Promise) {
|
|
175
|
+
coroutineScope.launch {
|
|
176
|
+
try {
|
|
177
|
+
OctopusSDK.overrideCommunityAccess(hasAccess)
|
|
178
|
+
promise.resolve(null)
|
|
179
|
+
} catch (e: Exception) {
|
|
180
|
+
promise.reject(
|
|
181
|
+
"OVERRIDE_ERROR",
|
|
182
|
+
e.message ?: "Failed to override community access",
|
|
183
|
+
e
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Tracks community access for analytics only. Does not change the actual access.
|
|
191
|
+
* Use when the app manages its own A/B logic and only needs to report the value.
|
|
192
|
+
*/
|
|
193
|
+
@ReactMethod
|
|
194
|
+
fun trackCommunityAccess(hasAccess: Boolean, promise: Promise) {
|
|
195
|
+
try {
|
|
196
|
+
OctopusSDK.trackAccessToCommunity(hasAccess)
|
|
197
|
+
promise.resolve(null)
|
|
198
|
+
} catch (e: Exception) {
|
|
199
|
+
promise.reject(
|
|
200
|
+
"TRACK_ACCESS_ERROR",
|
|
201
|
+
e.message ?: "Failed to track community access",
|
|
202
|
+
e
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Converts a ReadableMap to a Map<String, String>. Only string values are included;
|
|
209
|
+
* other types are skipped so that the result is valid for TrackerEvent.Custom properties.
|
|
210
|
+
*/
|
|
211
|
+
private fun readableMapToStringMap(map: ReadableMap?): Map<String, String> {
|
|
212
|
+
if (map == null) return emptyMap()
|
|
213
|
+
val result = mutableMapOf<String, String>()
|
|
214
|
+
val iterator = map.keySetIterator()
|
|
215
|
+
while (iterator.hasNextKey()) {
|
|
216
|
+
val key = iterator.nextKey()
|
|
217
|
+
if (map.getType(key) == ReadableType.String) {
|
|
218
|
+
map.getString(key)?.let { value -> result[key] = value }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return result
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private fun parseColor(colorString: String?): String? {
|
|
225
|
+
if (colorString == null) return null
|
|
226
|
+
|
|
227
|
+
return try {
|
|
228
|
+
// Validate that the color string is a valid hex color
|
|
229
|
+
android.graphics.Color.parseColor(colorString)
|
|
230
|
+
// Return the original string if parsing succeeds
|
|
231
|
+
colorString
|
|
232
|
+
} catch (e: IllegalArgumentException) {
|
|
233
|
+
// Invalid color format - return null to skip this color
|
|
234
|
+
null
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private fun parseFontsConfig(fontsMap: ReadableMap): OctopusFontsConfig? {
|
|
239
|
+
// Use pre-processed configuration from TypeScript layer
|
|
240
|
+
val parsedConfig = fontsMap.getMap("parsedConfig")
|
|
241
|
+
if (parsedConfig != null) {
|
|
242
|
+
return parsePreProcessedFontsConfig(parsedConfig)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return null
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private fun parsePreProcessedFontsConfig(parsedConfig: ReadableMap): OctopusFontsConfig? {
|
|
249
|
+
val textStylesMap = parsedConfig.getMap("textStyles")
|
|
250
|
+
val textStyles = mutableMapOf<String, OctopusTextStyleConfig>()
|
|
251
|
+
|
|
252
|
+
// Parse pre-processed font configuration from TypeScript layer
|
|
253
|
+
textStylesMap?.let { textStylesMap ->
|
|
254
|
+
val textStyleKeys = arrayOf("title1", "title2", "body1", "body2", "caption1", "caption2")
|
|
255
|
+
|
|
256
|
+
textStyleKeys.forEach { key ->
|
|
257
|
+
val textStyleMap = textStylesMap.getMap(key)
|
|
258
|
+
textStyleMap?.let { style ->
|
|
259
|
+
val fontType = style.getString("fontType")
|
|
260
|
+
val fontSize = if (style.hasKey("fontSize")) style.getDouble("fontSize") else Double.NaN
|
|
261
|
+
|
|
262
|
+
if (fontType != null || (!fontSize.isNaN() && fontSize > 0)) {
|
|
263
|
+
textStyles[key] = OctopusTextStyleConfig(
|
|
264
|
+
fontType = fontType,
|
|
265
|
+
fontSize = if (fontSize.isNaN() || fontSize <= 0) null else fontSize
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Only create fonts config if we have text styles
|
|
273
|
+
if (textStyles.isNotEmpty()) {
|
|
274
|
+
return OctopusFontsConfig(
|
|
275
|
+
textStyles = textStyles
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return null
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private fun startObservingReactiveEvents() {
|
|
283
|
+
startNotSeenNotificationsCollection()
|
|
284
|
+
startHasAccessToCommunityCollection()
|
|
285
|
+
startEventsCollection()
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private fun startNotSeenNotificationsCollection() {
|
|
289
|
+
notSeenNotificationsJob?.cancel()
|
|
290
|
+
notSeenNotificationsJob = coroutineScope.launch {
|
|
291
|
+
OctopusSDK.notSeenNotificationsCount.collect { count ->
|
|
292
|
+
eventEmitter.emitNotSeenNotificationsCountChanged(count)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private fun startHasAccessToCommunityCollection() {
|
|
298
|
+
hasAccessToCommunityJob?.cancel()
|
|
299
|
+
hasAccessToCommunityJob = coroutineScope.launch {
|
|
300
|
+
OctopusSDK.hasAccessToCommunity.collect { hasAccess ->
|
|
301
|
+
eventEmitter.emitHasAccessToCommunityChanged(hasAccess)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private fun startEventsCollection() {
|
|
307
|
+
eventsJob?.cancel()
|
|
308
|
+
eventsJob = coroutineScope.launch {
|
|
309
|
+
OctopusSDK.events.collect { event ->
|
|
310
|
+
val eventData = OctopusEventSerializer.serializeEvent(event)
|
|
311
|
+
if (eventData != null) {
|
|
312
|
+
eventEmitter.emitSDKEvent(eventData)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
companion object {
|
|
320
|
+
const val NAME = "OctopusReactNativeSdk"
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
init {
|
|
324
|
+
OctopusEventEmitter.instance = eventEmitter
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -6,12 +6,12 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
6
6
|
import com.facebook.react.uimanager.ViewManager
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class OctopusReactPackage : ReactPackage {
|
|
10
10
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
11
|
-
return listOf(
|
|
11
|
+
return listOf(OctopusReactModule(reactContext))
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
15
|
-
return
|
|
15
|
+
return listOf(OctopusUIViewManager(reactContext))
|
|
16
16
|
}
|
|
17
17
|
}
|