@viafoura/sdk-react-native 0.1.1 → 0.1.3

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.
@@ -45,4 +45,5 @@ android {
45
45
  dependencies {
46
46
  // Viafoura Android SDK (published artifact)
47
47
  implementation 'com.viafoura:android:2.0.52'
48
+ implementation 'androidx.fragment:fragment-ktx:1.7.1'
48
49
  }
@@ -0,0 +1,68 @@
1
+ package expo.modules.viafourasdk
2
+
3
+ import android.graphics.Color
4
+ import com.viafourasdk.src.Constants
5
+ import com.viafourasdk.src.model.local.VFColors
6
+ import com.viafourasdk.src.model.local.VFDefaultColors
7
+ import com.viafourasdk.src.model.local.VFTheme
8
+
9
+ private const val KEY_COLOR_PRIMARY = "colorPrimary"
10
+ private const val KEY_COLOR_PRIMARY_LIGHT = "colorPrimaryLight"
11
+ private const val KEY_COLOR_AVATARS = "colorAvatars"
12
+ private const val KEY_PRIMARY = "primary"
13
+ private const val KEY_PRIMARY_LIGHT = "primaryLight"
14
+ private const val KEY_AVATARS = "avatars"
15
+
16
+ internal fun resolveVFColors(colors: Map<String, Any?>?, theme: VFTheme?): VFColors {
17
+ val defaults = VFDefaultColors.getInstance()
18
+ val defaultPrimary = defaults.colorPrimaryDefault(null)
19
+ val defaultPrimaryLight = defaults.colorPrimaryLightDefault(null)
20
+
21
+ val primary =
22
+ parseColorOrNull(colors?.get(KEY_COLOR_PRIMARY) ?: colors?.get(KEY_PRIMARY))
23
+ ?: defaultPrimary
24
+ val primaryLight =
25
+ parseColorOrNull(colors?.get(KEY_COLOR_PRIMARY_LIGHT) ?: colors?.get(KEY_PRIMARY_LIGHT))
26
+ ?: defaultPrimaryLight
27
+
28
+ val vfColors = VFColors(primary, primaryLight)
29
+ parseAvatarColors(colors?.get(KEY_COLOR_AVATARS) ?: colors?.get(KEY_AVATARS))
30
+ ?.let { vfColors.setColorAvatars(it) }
31
+ theme?.let { vfColors.setTheme(it) }
32
+
33
+ return vfColors
34
+ }
35
+
36
+ private fun parseColorOrNull(value: Any?): Int? {
37
+ return when (value) {
38
+ is Int -> value
39
+ is Long -> value.toInt()
40
+ is Number -> value.toInt()
41
+ is String -> {
42
+ if (value.isBlank()) return null
43
+ try {
44
+ Color.parseColor(value)
45
+ } catch (_: IllegalArgumentException) {
46
+ null
47
+ }
48
+ }
49
+ else -> null
50
+ }
51
+ }
52
+
53
+ private fun parseAvatarColors(value: Any?): IntArray? {
54
+ val list = when (value) {
55
+ is List<*> -> value
56
+ is Array<*> -> value.asList()
57
+ else -> return null
58
+ }
59
+
60
+ if (list.size != Constants.AVATAR_COLORS.size) return null
61
+
62
+ val parsed = IntArray(list.size)
63
+ for (index in list.indices) {
64
+ val color = parseColorOrNull(list[index]) ?: return null
65
+ parsed[index] = color
66
+ }
67
+ return parsed
68
+ }
@@ -18,10 +18,11 @@ class NewCommentModule : Module() {
18
18
  Prop("articleUrl") { view: NewCommentView, v: String -> view.articleUrl = v }
19
19
  Prop("articleThumbnailUrl") { view: NewCommentView, v: String -> view.articleThumbnailUrl = v }
20
20
  Prop("darkMode") { view: NewCommentView, v: Boolean? -> view.darkMode = v ?: false }
21
+ Prop("theme") { view: NewCommentView, v: String? -> view.theme = v }
22
+ Prop("colors") { view: NewCommentView, v: Map<String, Any?>? -> view.colors = v }
21
23
 
22
24
  // Events
23
- Events("onAuthNeeded", "onCloseNewComment", "onHeightChanged")
25
+ Events("onAuthNeeded", "onCloseNewComment", "onHeightChanged", "onAction")
24
26
  }
25
27
  }
26
28
  }
27
-
@@ -20,14 +20,12 @@ import com.viafourasdk.src.interfaces.VFLayoutInterface
20
20
  import com.viafourasdk.src.model.local.VFActionData
21
21
  import com.viafourasdk.src.model.local.VFActionType
22
22
  import com.viafourasdk.src.model.local.VFArticleMetadata
23
- import com.viafourasdk.src.model.local.VFColors
24
- import com.viafourasdk.src.model.local.VFDefaultColors
25
23
  import com.viafourasdk.src.model.local.VFSettings
26
24
  import com.viafourasdk.src.model.local.VFTheme
27
25
  import java.net.URL
28
26
  import java.util.UUID
29
27
 
30
- class NewCommentView(context: Context, private val appContext: AppContext) :
28
+ class NewCommentView(context: Context, appContext: AppContext) :
31
29
  ExpoView(context, appContext), VFCustomUIInterface, VFActionsInterface, VFLayoutInterface {
32
30
 
33
31
  // Props
@@ -40,11 +38,22 @@ class NewCommentView(context: Context, private val appContext: AppContext) :
40
38
  var articleUrl: String? = null
41
39
  var articleThumbnailUrl: String? = null
42
40
  var darkMode: Boolean = false
41
+ set(value) {
42
+ field = value
43
+ applyThemeIfReady()
44
+ }
45
+ var theme: String? = null
46
+ set(value) {
47
+ field = value
48
+ applyThemeIfReady()
49
+ }
50
+ var colors: Map<String, Any?>? = null
43
51
 
44
52
  // Events
45
53
  private val onAuthNeeded by EventDispatcher()
46
54
  private val onCloseNewComment by EventDispatcher()
47
55
  private val onHeightChanged by EventDispatcher()
56
+ private val onAction by EventDispatcher()
48
57
 
49
58
  // Internals
50
59
  private val container = FrameLayout(context).also {
@@ -72,17 +81,16 @@ class NewCommentView(context: Context, private val appContext: AppContext) :
72
81
  val activity = currentActivity() ?: return
73
82
 
74
83
  try {
84
+ val resolvedTheme = resolveTheme()
75
85
  val metadata = VFArticleMetadata(
76
86
  URL(requireNotNull(articleUrl)),
77
87
  requireNotNull(articleTitle),
78
88
  articleSubtitle ?: "",
79
89
  URL(requireNotNull(articleThumbnailUrl))
80
90
  )
81
- val colors = VFColors(
82
- VFDefaultColors.getInstance().colorPrimaryDefault(null),
83
- VFDefaultColors.getInstance().colorPrimaryLightDefault(null)
91
+ val settings = VFSettings(
92
+ resolveVFColors(colors, resolvedTheme)
84
93
  )
85
- val settings = VFSettings(colors)
86
94
 
87
95
  val type = when (newCommentActionType) {
88
96
  "edit" -> VFNewCommentAction.VFNewCommentActionType.edit
@@ -99,7 +107,7 @@ class NewCommentView(context: Context, private val appContext: AppContext) :
99
107
  val frag = builder.build()
100
108
  frag.setActionCallback(this)
101
109
  frag.setCustomUICallback(this)
102
- frag.setTheme(if (darkMode) VFTheme.dark else VFTheme.light)
110
+ frag.setTheme(resolvedTheme)
103
111
 
104
112
  activity.supportFragmentManager
105
113
  .beginTransaction()
@@ -127,15 +135,18 @@ class NewCommentView(context: Context, private val appContext: AppContext) :
127
135
 
128
136
  // VFActionsInterface
129
137
  override fun onNewAction(actionType: VFActionType, action: VFActionData) {
138
+ val actionPayload = mutableMapOf<String, Any>("type" to actionType.toString())
130
139
  when (actionType) {
131
140
  VFActionType.closeNewCommentPressed -> {
132
141
  onCloseNewComment(emptyMap<String, Any>())
133
142
  }
134
143
  VFActionType.authPressed -> {
144
+ actionPayload["requireLogin"] = true
135
145
  onAuthNeeded(mapOf("requireLogin" to true))
136
146
  }
137
147
  else -> {}
138
148
  }
149
+ onAction(actionPayload)
139
150
  }
140
151
 
141
152
  // VFCustomUIInterface
@@ -149,5 +160,16 @@ class NewCommentView(context: Context, private val appContext: AppContext) :
149
160
  override fun containerHeightUpdated(fragment: VFFragment, containerId: String, height: Int) {
150
161
  onHeightChanged(mapOf("newHeight" to height, "containerId" to containerId))
151
162
  }
152
- }
153
163
 
164
+ private fun resolveTheme(): VFTheme {
165
+ return when (theme?.lowercase()) {
166
+ "dark" -> VFTheme.dark
167
+ "light" -> VFTheme.light
168
+ else -> if (darkMode) VFTheme.dark else VFTheme.light
169
+ }
170
+ }
171
+
172
+ private fun applyThemeIfReady() {
173
+ fragment?.setTheme(resolveTheme())
174
+ }
175
+ }
@@ -17,10 +17,11 @@ class PreviewCommentsModule : Module() {
17
17
  Prop("articleThumbnailUrl") { view: PreviewCommentsView, v: String -> view.articleThumbnailUrl = v }
18
18
  Prop("syndicationKey") { view: PreviewCommentsView, v: String? -> view.syndicationKey = v }
19
19
  Prop("darkMode") { view: PreviewCommentsView, v: Boolean? -> view.darkMode = v ?: false }
20
+ Prop("theme") { view: PreviewCommentsView, v: String? -> view.theme = v }
21
+ Prop("colors") { view: PreviewCommentsView, v: Map<String, Any?>? -> view.colors = v }
20
22
 
21
23
  // Events
22
- Events("onHeightChanged", "onAuthNeeded", "onOpenProfile", "onNewComment", "onArticlePressed")
24
+ Events("onHeightChanged", "onAuthNeeded", "onOpenProfile", "onNewComment", "onArticlePressed", "onAction")
23
25
  }
24
26
  }
25
27
  }
26
-
@@ -20,13 +20,11 @@ import com.viafourasdk.src.interfaces.VFLayoutInterface
20
20
  import com.viafourasdk.src.model.local.VFActionData
21
21
  import com.viafourasdk.src.model.local.VFActionType
22
22
  import com.viafourasdk.src.model.local.VFArticleMetadata
23
- import com.viafourasdk.src.model.local.VFColors
24
- import com.viafourasdk.src.model.local.VFDefaultColors
25
23
  import com.viafourasdk.src.model.local.VFSettings
26
24
  import com.viafourasdk.src.model.local.VFTheme
27
25
  import java.net.URL
28
26
 
29
- class PreviewCommentsView(context: Context, private val appContext: AppContext) :
27
+ class PreviewCommentsView(context: Context, appContext: AppContext) :
30
28
  ExpoView(context, appContext), VFCustomUIInterface, VFActionsInterface, VFLayoutInterface {
31
29
 
32
30
  // Props
@@ -38,6 +36,16 @@ class PreviewCommentsView(context: Context, private val appContext: AppContext)
38
36
  var articleThumbnailUrl: String? = null
39
37
  var syndicationKey: String? = null
40
38
  var darkMode: Boolean = false
39
+ set(value) {
40
+ field = value
41
+ applyThemeIfReady()
42
+ }
43
+ var theme: String? = null
44
+ set(value) {
45
+ field = value
46
+ applyThemeIfReady()
47
+ }
48
+ var colors: Map<String, Any?>? = null
41
49
 
42
50
  // Events
43
51
  private val onHeightChanged by EventDispatcher()
@@ -45,6 +53,7 @@ class PreviewCommentsView(context: Context, private val appContext: AppContext)
45
53
  private val onOpenProfile by EventDispatcher()
46
54
  private val onNewComment by EventDispatcher()
47
55
  private val onArticlePressed by EventDispatcher()
56
+ private val onAction by EventDispatcher()
48
57
 
49
58
  // Internals
50
59
  private val container = FrameLayout(context).also {
@@ -73,17 +82,16 @@ class PreviewCommentsView(context: Context, private val appContext: AppContext)
73
82
  val activity = currentActivity() ?: return
74
83
 
75
84
  try {
85
+ val resolvedTheme = resolveTheme()
76
86
  val meta = VFArticleMetadata(
77
87
  URL(requireNotNull(articleUrl)),
78
88
  requireNotNull(articleTitle),
79
89
  articleSubtitle ?: "",
80
90
  URL(requireNotNull(articleThumbnailUrl))
81
91
  )
82
- val colors = VFColors(
83
- VFDefaultColors.getInstance().colorPrimaryDefault(null),
84
- VFDefaultColors.getInstance().colorPrimaryLightDefault(null)
92
+ val settings = VFSettings(
93
+ resolveVFColors(colors, resolvedTheme)
85
94
  )
86
- val settings = VFSettings(colors)
87
95
 
88
96
  val builder = VFPreviewCommentsFragmentBuilder(requireNotNull(containerId), meta, settings)
89
97
  syndicationKey?.let { builder.syndicationKey(it) }
@@ -91,7 +99,7 @@ class PreviewCommentsView(context: Context, private val appContext: AppContext)
91
99
  frag.setActionCallback(this)
92
100
  frag.setLayoutCallback(this)
93
101
  frag.setCustomUICallback(this)
94
- frag.setTheme(if (darkMode) VFTheme.dark else VFTheme.light)
102
+ frag.setTheme(resolvedTheme)
95
103
  authorId?.let { if (it.isNotEmpty()) frag.setAuthorIds(listOf(it)) }
96
104
 
97
105
  activity.supportFragmentManager
@@ -127,31 +135,54 @@ class PreviewCommentsView(context: Context, private val appContext: AppContext)
127
135
 
128
136
  // VFActionsInterface
129
137
  override fun onNewAction(actionType: VFActionType, action: VFActionData) {
138
+ val actionPayload = mutableMapOf<String, Any>("type" to actionType.toString())
130
139
  when (actionType) {
131
140
  VFActionType.writeNewCommentPressed -> {
132
- val content = action.newCommentAction?.content?.toString()
133
- val type = action.newCommentAction?.type?.toString()
134
- onNewComment(mapOf("content" to content, "actionType" to type).filterValues { it != null })
141
+ val payload = mutableMapOf<String, Any>()
142
+ action.newCommentAction?.content?.toString()?.let { payload["content"] = it }
143
+ action.newCommentAction?.type?.toString()?.let { payload["actionType"] = it }
144
+ actionPayload.putAll(payload)
145
+ onNewComment(payload)
135
146
  }
136
147
  VFActionType.openProfilePressed -> {
137
- val pres = action.openProfileAction?.presentationType?.toString()
138
- val user = action.openProfileAction?.userUUID?.toString()
139
- onOpenProfile(mapOf("presentationType" to pres, "userUUID" to user).filterValues { it != null })
148
+ val payload = mutableMapOf<String, Any>()
149
+ action.openProfileAction?.presentationType?.toString()?.let { payload["presentationType"] = it }
150
+ action.openProfileAction?.userUUID?.toString()?.let { payload["userUUID"] = it }
151
+ actionPayload.putAll(payload)
152
+ onOpenProfile(payload)
140
153
  }
141
154
  VFActionType.trendingArticlePressed -> {
142
- val url = action.trendingPressedAction?.articleMetadata?.url?.toString() ?: return
155
+ val url = action.trendingPressedAction?.articleMetadata?.url?.toString()
143
156
  val containerId = action.trendingPressedAction?.containerId ?: ""
144
- onArticlePressed(mapOf("articleUrl" to url, "containerId" to containerId))
157
+ url?.let { actionPayload["articleUrl"] = it }
158
+ actionPayload["containerId"] = containerId
159
+ if (url != null) {
160
+ onArticlePressed(mapOf("articleUrl" to url, "containerId" to containerId))
161
+ }
145
162
  }
146
163
  VFActionType.authPressed -> {
164
+ actionPayload["requireLogin"] = true
147
165
  onAuthNeeded(mapOf("requireLogin" to true))
148
166
  }
149
167
  else -> {}
150
168
  }
169
+ onAction(actionPayload)
151
170
  }
152
171
 
153
172
  // VFLayoutInterface
154
173
  override fun containerHeightUpdated(fragment: VFFragment, containerId: String, height: Int) {
155
174
  onHeightChanged(mapOf("newHeight" to height, "containerId" to containerId))
156
175
  }
176
+
177
+ private fun resolveTheme(): VFTheme {
178
+ return when (theme?.lowercase()) {
179
+ "dark" -> VFTheme.dark
180
+ "light" -> VFTheme.light
181
+ else -> if (darkMode) VFTheme.dark else VFTheme.light
182
+ }
183
+ }
184
+
185
+ private fun applyThemeIfReady() {
186
+ fragment?.setTheme(resolveTheme())
187
+ }
157
188
  }
@@ -12,10 +12,11 @@ class ProfileModule : Module() {
12
12
  Prop("userUUID") { view: ProfileView, v: String -> view.userUUID = v }
13
13
  Prop("presentationType") { view: ProfileView, v: String? -> view.presentationType = v }
14
14
  Prop("darkMode") { view: ProfileView, v: Boolean? -> view.darkMode = v ?: false }
15
+ Prop("theme") { view: ProfileView, v: String? -> view.theme = v }
16
+ Prop("colors") { view: ProfileView, v: Map<String, Any?>? -> view.colors = v }
15
17
 
16
18
  // Events
17
- Events("onAuthNeeded", "onCloseProfile")
19
+ Events("onAuthNeeded", "onCloseProfile", "onAction")
18
20
  }
19
21
  }
20
22
  }
21
-
@@ -17,24 +17,33 @@ import com.viafourasdk.src.interfaces.VFCustomUIInterface
17
17
  import com.viafourasdk.src.interfaces.VFLayoutInterface
18
18
  import com.viafourasdk.src.model.local.VFActionData
19
19
  import com.viafourasdk.src.model.local.VFActionType
20
- import com.viafourasdk.src.model.local.VFDefaultColors
21
- import com.viafourasdk.src.model.local.VFColors
22
20
  import com.viafourasdk.src.model.local.VFSettings
23
21
  import com.viafourasdk.src.model.local.VFProfilePresentationType
24
22
  import com.viafourasdk.src.model.local.VFTheme
25
23
  import java.util.UUID
26
24
 
27
- class ProfileView(context: Context, private val appContext: AppContext) :
25
+ class ProfileView(context: Context, appContext: AppContext) :
28
26
  ExpoView(context, appContext), VFCustomUIInterface, VFActionsInterface, VFLayoutInterface {
29
27
 
30
28
  // Props
31
29
  var userUUID: String? = null
32
30
  var presentationType: String? = null // "profile" | "feed"
33
31
  var darkMode: Boolean = false
32
+ set(value) {
33
+ field = value
34
+ applyThemeIfReady()
35
+ }
36
+ var theme: String? = null
37
+ set(value) {
38
+ field = value
39
+ applyThemeIfReady()
40
+ }
41
+ var colors: Map<String, Any?>? = null
34
42
 
35
43
  // Events
36
44
  private val onAuthNeeded by EventDispatcher()
37
45
  private val onCloseProfile by EventDispatcher()
46
+ private val onAction by EventDispatcher()
38
47
 
39
48
  // Internals
40
49
  private val container = FrameLayout(context).also {
@@ -62,23 +71,21 @@ class ProfileView(context: Context, private val appContext: AppContext) :
62
71
  val activity = currentActivity() ?: return
63
72
 
64
73
  try {
65
- val colors = VFColors(
66
- VFDefaultColors.getInstance().colorPrimaryDefault(null),
67
- VFDefaultColors.getInstance().colorPrimaryLightDefault(null)
74
+ val resolvedTheme = resolveTheme()
75
+ val settings = VFSettings(
76
+ resolveVFColors(colors, resolvedTheme)
68
77
  )
69
- val settings = VFSettings(colors)
70
78
 
71
79
  val pres = when (presentationType) {
72
80
  "feed" -> VFProfilePresentationType.feed
73
- "profile" -> VFProfilePresentationType.profile
74
- else -> null
81
+ else -> VFProfilePresentationType.profile
75
82
  }
76
83
 
77
84
  val uuid = UUID.fromString(requireNotNull(userUUID))
78
85
  val frag = VFProfileFragmentBuilder(uuid, pres, settings).build()
79
86
  frag.setActionCallback(this)
80
87
  frag.setCustomUICallback(this)
81
- frag.setTheme(if (darkMode) VFTheme.dark else VFTheme.light)
88
+ frag.setTheme(resolvedTheme)
82
89
 
83
90
  activity.supportFragmentManager
84
91
  .beginTransaction()
@@ -106,15 +113,18 @@ class ProfileView(context: Context, private val appContext: AppContext) :
106
113
 
107
114
  // VFActionsInterface
108
115
  override fun onNewAction(actionType: VFActionType, action: VFActionData) {
116
+ val actionPayload = mutableMapOf<String, Any>("type" to actionType.toString())
109
117
  when (actionType) {
110
118
  VFActionType.closeProfilePressed -> {
111
119
  onCloseProfile(emptyMap<String, Any>())
112
120
  }
113
121
  VFActionType.authPressed -> {
122
+ actionPayload["requireLogin"] = true
114
123
  onAuthNeeded(mapOf("requireLogin" to true))
115
124
  }
116
125
  else -> {}
117
126
  }
127
+ onAction(actionPayload)
118
128
  }
119
129
 
120
130
  // VFCustomUIInterface
@@ -130,5 +140,16 @@ class ProfileView(context: Context, private val appContext: AppContext) :
130
140
  containerId: String,
131
141
  height: Int
132
142
  ) { /* no-op for profile */ }
133
- }
134
143
 
144
+ private fun resolveTheme(): VFTheme {
145
+ return when (theme?.lowercase()) {
146
+ "dark" -> VFTheme.dark
147
+ "light" -> VFTheme.light
148
+ else -> if (darkMode) VFTheme.dark else VFTheme.light
149
+ }
150
+ }
151
+
152
+ private fun applyThemeIfReady() {
153
+ fragment?.setTheme(resolveTheme())
154
+ }
155
+ }
@@ -36,7 +36,7 @@ class ViafouraModule : Module() {
36
36
  promise.resolve(null)
37
37
  }
38
38
  override fun onError(err: NetworkError) {
39
- promise.reject("E_VF_LOGIN", err.message ?: "Login failed")
39
+ promise.reject("E_VF_LOGIN", err.message ?: "Login failed", null)
40
40
  }
41
41
  })
42
42
  }
@@ -47,7 +47,7 @@ class ViafouraModule : Module() {
47
47
  promise.resolve(null)
48
48
  }
49
49
  override fun onError(err: NetworkError) {
50
- promise.reject("E_VF_SIGNUP", err.message ?: "Signup failed")
50
+ promise.reject("E_VF_SIGNUP", err.message ?: "Signup failed", null)
51
51
  }
52
52
  })
53
53
  }
@@ -59,7 +59,7 @@ class ViafouraModule : Module() {
59
59
  promise.resolve(null)
60
60
  }
61
61
  override fun onError(err: NetworkError) {
62
- promise.reject("E_VF_SOCIAL", err.message ?: "Social login failed")
62
+ promise.reject("E_VF_SOCIAL", err.message ?: "Social login failed", null)
63
63
  }
64
64
  })
65
65
  }
@@ -70,7 +70,7 @@ class ViafouraModule : Module() {
70
70
  promise.resolve(null)
71
71
  }
72
72
  override fun onError(err: NetworkError) {
73
- promise.reject("E_VF_OPENID", err.message ?: "OpenID login failed")
73
+ promise.reject("E_VF_OPENID", err.message ?: "OpenID login failed", null)
74
74
  }
75
75
  })
76
76
  }
@@ -81,7 +81,7 @@ class ViafouraModule : Module() {
81
81
  promise.resolve(null)
82
82
  }
83
83
  override fun onError(err: NetworkError) {
84
- promise.reject("E_VF_COOKIE", err.message ?: "Cookie login failed")
84
+ promise.reject("E_VF_COOKIE", err.message ?: "Cookie login failed", null)
85
85
  }
86
86
  })
87
87
  }
@@ -92,7 +92,7 @@ class ViafouraModule : Module() {
92
92
  promise.resolve(null)
93
93
  }
94
94
  override fun onError(err: NetworkError) {
95
- promise.reject("E_VF_RESET", err.message ?: "Password reset failed")
95
+ promise.reject("E_VF_RESET", err.message ?: "Password reset failed", null)
96
96
  }
97
97
  })
98
98
  }
@@ -102,11 +102,11 @@ class ViafouraModule : Module() {
102
102
  val context = appContext.reactContext?.applicationContext
103
103
  ?: appContext.currentActivity?.applicationContext
104
104
  if (context == null) {
105
- promise.reject("E_VF_INIT", "No application context available")
105
+ promise.reject("E_VF_INIT", "No application context available", null)
106
106
  return@AsyncFunction
107
107
  }
108
108
  if (siteUUID.isBlank() || siteDomain.isBlank()) {
109
- promise.reject("E_VF_INIT", "Invalid Viafoura initialization parameters")
109
+ promise.reject("E_VF_INIT", "Invalid Viafoura initialization parameters", null)
110
110
  return@AsyncFunction
111
111
  }
112
112
  try {
@@ -114,7 +114,7 @@ class ViafouraModule : Module() {
114
114
  ViafouraSDK.initialize(context, siteUUID, siteDomain)
115
115
  promise.resolve(null)
116
116
  } catch (e: Exception) {
117
- promise.reject("E_VF_INIT", e.message ?: "Viafoura initialization failed")
117
+ promise.reject("E_VF_INIT", e.message ?: "Viafoura initialization failed", e)
118
118
  }
119
119
  }
120
120
 
@@ -5,7 +5,49 @@ export type ViafouraModuleEvents = {
5
5
  export type ChangeEventPayload = {
6
6
  value: string;
7
7
  };
8
- export type PreviewCommentsEvents = 'onHeightChanged' | 'onAuthNeeded' | 'onOpenProfile' | 'onNewComment' | 'onArticlePressed';
8
+ export type PreviewCommentsEvents = 'onHeightChanged' | 'onAuthNeeded' | 'onOpenProfile' | 'onNewComment' | 'onArticlePressed' | 'onAction';
9
+ export type ViafouraColors = {
10
+ colorPrimary?: string;
11
+ colorPrimaryLight?: string;
12
+ colorAvatars?: string[];
13
+ };
14
+ export type ActionCallbackType = 'writeNewCommentPressed' | 'trendingArticlePressed' | 'openProfilePressed' | 'seeMoreCommentsPressed' | 'notificationPressed' | 'commentPosted' | 'replyPosted' | 'authPressed' | 'closeNewCommentPressed' | 'closeProfilePressed';
15
+ export type ActionCallbackPayload = {
16
+ type: 'writeNewCommentPressed';
17
+ actionType?: 'create' | 'edit' | 'reply';
18
+ content?: string;
19
+ } | {
20
+ type: 'trendingArticlePressed';
21
+ containerId?: string;
22
+ articleUrl?: string;
23
+ } | {
24
+ type: 'openProfilePressed';
25
+ userUUID?: string;
26
+ presentationType?: 'profile' | 'feed';
27
+ } | {
28
+ type: 'seeMoreCommentsPressed';
29
+ } | {
30
+ type: 'notificationPressed';
31
+ presentationType?: 'profile' | 'content';
32
+ userUUID?: string;
33
+ containerUUID?: string;
34
+ contentUUID?: string;
35
+ containerId?: string;
36
+ articleUrl?: string;
37
+ } | {
38
+ type: 'commentPosted';
39
+ content?: string;
40
+ } | {
41
+ type: 'replyPosted';
42
+ content?: string;
43
+ } | {
44
+ type: 'authPressed';
45
+ requireLogin?: boolean;
46
+ } | {
47
+ type: 'closeNewCommentPressed';
48
+ } | {
49
+ type: 'closeProfilePressed';
50
+ };
9
51
  export type PreviewCommentsHeightChangedPayload = {
10
52
  newHeight: number;
11
53
  containerId: string;
@@ -34,6 +76,8 @@ export type PreviewCommentsViewProps = {
34
76
  articleThumbnailUrl: string;
35
77
  syndicationKey?: string;
36
78
  darkMode?: boolean;
79
+ theme?: 'light' | 'dark';
80
+ colors?: ViafouraColors;
37
81
  onHeightChanged?: (event: {
38
82
  nativeEvent: PreviewCommentsHeightChangedPayload;
39
83
  }) => void;
@@ -49,6 +93,9 @@ export type PreviewCommentsViewProps = {
49
93
  onArticlePressed?: (event: {
50
94
  nativeEvent: PreviewCommentsArticlePressedPayload;
51
95
  }) => void;
96
+ onAction?: (event: {
97
+ nativeEvent: ActionCallbackPayload;
98
+ }) => void;
52
99
  style?: StyleProp<ViewStyle>;
53
100
  };
54
101
  export type ProfileAuthNeededPayload = {
@@ -58,12 +105,17 @@ export type ProfileViewProps = {
58
105
  userUUID: string;
59
106
  presentationType?: 'profile' | 'feed';
60
107
  darkMode?: boolean;
108
+ theme?: 'light' | 'dark';
109
+ colors?: ViafouraColors;
61
110
  onAuthNeeded?: (event: {
62
111
  nativeEvent: ProfileAuthNeededPayload;
63
112
  }) => void;
64
113
  onCloseProfile?: (event: {
65
114
  nativeEvent: Record<string, never>;
66
115
  }) => void;
116
+ onAction?: (event: {
117
+ nativeEvent: ActionCallbackPayload;
118
+ }) => void;
67
119
  style?: StyleProp<ViewStyle>;
68
120
  };
69
121
  export type NewCommentHeightChangedPayload = PreviewCommentsHeightChangedPayload;
@@ -78,6 +130,8 @@ export type NewCommentViewProps = {
78
130
  articleUrl: string;
79
131
  articleThumbnailUrl: string;
80
132
  darkMode?: boolean;
133
+ theme?: 'light' | 'dark';
134
+ colors?: ViafouraColors;
81
135
  onHeightChanged?: (event: {
82
136
  nativeEvent: NewCommentHeightChangedPayload;
83
137
  }) => void;
@@ -87,6 +141,9 @@ export type NewCommentViewProps = {
87
141
  onCloseNewComment?: (event: {
88
142
  nativeEvent: Record<string, never>;
89
143
  }) => void;
144
+ onAction?: (event: {
145
+ nativeEvent: ActionCallbackPayload;
146
+ }) => void;
90
147
  style?: StyleProp<ViewStyle>;
91
148
  };
92
149
  //# sourceMappingURL=Viafoura.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Viafoura.types.d.ts","sourceRoot":"","sources":["../src/Viafoura.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Viafoura.types.d.ts","sourceRoot":"","sources":["../src/Viafoura.types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Viafoura.types.js","sourceRoot":"","sources":["../src/Viafoura.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { StyleProp, ViewStyle } from 'react-native';\n\nexport type ViafouraModuleEvents = {\n onChange: (params: ChangeEventPayload) => void;\n};\n\nexport type ChangeEventPayload = {\n value: string;\n};\n\n\nexport type PreviewCommentsEvents =\n | 'onHeightChanged'\n | 'onAuthNeeded'\n | 'onOpenProfile'\n | 'onNewComment'\n | 'onArticlePressed';\n\nexport type PreviewCommentsHeightChangedPayload = {\n newHeight: number;\n containerId: string;\n};\n\nexport type PreviewCommentsAuthNeededPayload = {\n requireLogin?: boolean;\n};\n\nexport type PreviewCommentsOpenProfilePayload = {\n userUUID: string;\n presentationType?: string;\n};\n\nexport type PreviewCommentsNewCommentPayload = {\n content?: string;\n actionType?: string;\n};\n\nexport type PreviewCommentsArticlePressedPayload = {\n articleUrl: string;\n containerId: string;\n};\n\nexport type PreviewCommentsViewProps = {\n containerId: string;\n authorId?: string;\n articleUrl: string;\n articleTitle: string;\n articleSubtitle?: string;\n articleThumbnailUrl: string;\n syndicationKey?: string;\n darkMode?: boolean;\n onHeightChanged?: (event: { nativeEvent: PreviewCommentsHeightChangedPayload }) => void;\n onAuthNeeded?: (event: { nativeEvent: PreviewCommentsAuthNeededPayload }) => void;\n onOpenProfile?: (event: { nativeEvent: PreviewCommentsOpenProfilePayload }) => void;\n onNewComment?: (event: { nativeEvent: PreviewCommentsNewCommentPayload }) => void;\n onArticlePressed?: (event: { nativeEvent: PreviewCommentsArticlePressedPayload }) => void;\n style?: StyleProp<ViewStyle>;\n};\n\nexport type ProfileAuthNeededPayload = {\n requireLogin?: boolean;\n};\n\nexport type ProfileViewProps = {\n userUUID: string;\n presentationType?: 'profile' | 'feed';\n darkMode?: boolean;\n onAuthNeeded?: (event: { nativeEvent: ProfileAuthNeededPayload }) => void;\n onCloseProfile?: (event: { nativeEvent: Record<string, never> }) => void;\n style?: StyleProp<ViewStyle>;\n};\n\nexport type NewCommentHeightChangedPayload = PreviewCommentsHeightChangedPayload;\nexport type NewCommentAuthNeededPayload = PreviewCommentsAuthNeededPayload;\n\nexport type NewCommentViewProps = {\n newCommentActionType: 'create' | 'edit' | 'reply';\n content?: string; // UUID as string\n containerId: string;\n syndicationKey?: string;\n articleTitle: string;\n articleSubtitle?: string;\n articleUrl: string;\n articleThumbnailUrl: string;\n darkMode?: boolean;\n onHeightChanged?: (event: { nativeEvent: NewCommentHeightChangedPayload }) => void;\n onAuthNeeded?: (event: { nativeEvent: NewCommentAuthNeededPayload }) => void;\n onCloseNewComment?: (event: { nativeEvent: Record<string, never> }) => void;\n style?: StyleProp<ViewStyle>;\n};\n"]}
1
+ {"version":3,"file":"Viafoura.types.js","sourceRoot":"","sources":["../src/Viafoura.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { StyleProp, ViewStyle } from 'react-native';\n\nexport type ViafouraModuleEvents = {\n onChange: (params: ChangeEventPayload) => void;\n};\n\nexport type ChangeEventPayload = {\n value: string;\n};\n\n\nexport type PreviewCommentsEvents =\n | 'onHeightChanged'\n | 'onAuthNeeded'\n | 'onOpenProfile'\n | 'onNewComment'\n | 'onArticlePressed'\n | 'onAction';\n\nexport type ViafouraColors = {\n colorPrimary?: string;\n colorPrimaryLight?: string;\n colorAvatars?: string[];\n};\n\nexport type ActionCallbackType =\n | 'writeNewCommentPressed'\n | 'trendingArticlePressed'\n | 'openProfilePressed'\n | 'seeMoreCommentsPressed'\n | 'notificationPressed'\n | 'commentPosted'\n | 'replyPosted'\n | 'authPressed'\n | 'closeNewCommentPressed'\n | 'closeProfilePressed';\n\nexport type ActionCallbackPayload =\n | {\n type: 'writeNewCommentPressed';\n actionType?: 'create' | 'edit' | 'reply';\n content?: string;\n }\n | {\n type: 'trendingArticlePressed';\n containerId?: string;\n articleUrl?: string;\n }\n | {\n type: 'openProfilePressed';\n userUUID?: string;\n presentationType?: 'profile' | 'feed';\n }\n | {\n type: 'seeMoreCommentsPressed';\n }\n | {\n type: 'notificationPressed';\n presentationType?: 'profile' | 'content';\n userUUID?: string;\n containerUUID?: string;\n contentUUID?: string;\n containerId?: string;\n articleUrl?: string;\n }\n | {\n type: 'commentPosted';\n content?: string;\n }\n | {\n type: 'replyPosted';\n content?: string;\n }\n | {\n type: 'authPressed';\n requireLogin?: boolean;\n }\n | {\n type: 'closeNewCommentPressed';\n }\n | {\n type: 'closeProfilePressed';\n };\n\nexport type PreviewCommentsHeightChangedPayload = {\n newHeight: number;\n containerId: string;\n};\n\nexport type PreviewCommentsAuthNeededPayload = {\n requireLogin?: boolean;\n};\n\nexport type PreviewCommentsOpenProfilePayload = {\n userUUID: string;\n presentationType?: string;\n};\n\nexport type PreviewCommentsNewCommentPayload = {\n content?: string;\n actionType?: string;\n};\n\nexport type PreviewCommentsArticlePressedPayload = {\n articleUrl: string;\n containerId: string;\n};\n\nexport type PreviewCommentsViewProps = {\n containerId: string;\n authorId?: string;\n articleUrl: string;\n articleTitle: string;\n articleSubtitle?: string;\n articleThumbnailUrl: string;\n syndicationKey?: string;\n darkMode?: boolean;\n theme?: 'light' | 'dark';\n colors?: ViafouraColors;\n onHeightChanged?: (event: { nativeEvent: PreviewCommentsHeightChangedPayload }) => void;\n onAuthNeeded?: (event: { nativeEvent: PreviewCommentsAuthNeededPayload }) => void;\n onOpenProfile?: (event: { nativeEvent: PreviewCommentsOpenProfilePayload }) => void;\n onNewComment?: (event: { nativeEvent: PreviewCommentsNewCommentPayload }) => void;\n onArticlePressed?: (event: { nativeEvent: PreviewCommentsArticlePressedPayload }) => void;\n onAction?: (event: { nativeEvent: ActionCallbackPayload }) => void;\n style?: StyleProp<ViewStyle>;\n};\n\nexport type ProfileAuthNeededPayload = {\n requireLogin?: boolean;\n};\n\nexport type ProfileViewProps = {\n userUUID: string;\n presentationType?: 'profile' | 'feed';\n darkMode?: boolean;\n theme?: 'light' | 'dark';\n colors?: ViafouraColors;\n onAuthNeeded?: (event: { nativeEvent: ProfileAuthNeededPayload }) => void;\n onCloseProfile?: (event: { nativeEvent: Record<string, never> }) => void;\n onAction?: (event: { nativeEvent: ActionCallbackPayload }) => void;\n style?: StyleProp<ViewStyle>;\n};\n\nexport type NewCommentHeightChangedPayload = PreviewCommentsHeightChangedPayload;\nexport type NewCommentAuthNeededPayload = PreviewCommentsAuthNeededPayload;\n\nexport type NewCommentViewProps = {\n newCommentActionType: 'create' | 'edit' | 'reply';\n content?: string; // UUID as string\n containerId: string;\n syndicationKey?: string;\n articleTitle: string;\n articleSubtitle?: string;\n articleUrl: string;\n articleThumbnailUrl: string;\n darkMode?: boolean;\n theme?: 'light' | 'dark';\n colors?: ViafouraColors;\n onHeightChanged?: (event: { nativeEvent: NewCommentHeightChangedPayload }) => void;\n onAuthNeeded?: (event: { nativeEvent: NewCommentAuthNeededPayload }) => void;\n onCloseNewComment?: (event: { nativeEvent: Record<string, never> }) => void;\n onAction?: (event: { nativeEvent: ActionCallbackPayload }) => void;\n style?: StyleProp<ViewStyle>;\n};\n"]}
@@ -13,7 +13,17 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
13
13
  var syndicationKey: String = ""
14
14
  var articleSubtitle: String = ""
15
15
  var articleThumbnailUrl: String = ""
16
- var darkMode: Bool = false
16
+ var darkMode: Bool = false {
17
+ didSet {
18
+ applyThemeIfReady()
19
+ }
20
+ }
21
+ var theme: String? = nil {
22
+ didSet {
23
+ applyThemeIfReady()
24
+ }
25
+ }
26
+ var colors: [String: Any] = [:]
17
27
 
18
28
  // Events
19
29
  let onHeightChanged = EventDispatcher()
@@ -21,6 +31,7 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
21
31
  let onOpenProfile = EventDispatcher()
22
32
  let onNewComment = EventDispatcher()
23
33
  let onArticlePressed = EventDispatcher()
34
+ let onAction = EventDispatcher()
24
35
 
25
36
  // Internals
26
37
  let fontBold = UIFont.boldSystemFont(ofSize: 17)
@@ -39,10 +50,13 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
39
50
  }
40
51
 
41
52
  private func initializeSettings() {
42
- let colors = VFColors(
43
- colorPrimary: UIColor(red: 0.00, green: 0.45, blue: 0.91, alpha: 1.00),
44
- colorPrimaryLight: UIColor(red: 0.90, green: 0.95, blue: 1.00, alpha: 1.00)
53
+ let resolvedTheme = resolveTheme()
54
+ var colors = VFColors(
55
+ colorPrimary: resolveColor(key: "colorPrimary", fallbackKey: "primary", fallback: UIColor(red: 0.00, green: 0.45, blue: 0.91, alpha: 1.00)),
56
+ colorPrimaryLight: resolveColor(key: "colorPrimaryLight", fallbackKey: "primaryLight", fallback: UIColor(red: 0.90, green: 0.95, blue: 1.00, alpha: 1.00)),
57
+ colorAvatars: resolveAvatarColors() ?? Constants.AvatarColors.colors
45
58
  )
59
+ colors.setTheme(theme: resolvedTheme)
46
60
  let fonts = VFFonts(fontBold: fontBold)
47
61
  settings = VFSettings(colors: colors, fonts: fonts)
48
62
 
@@ -68,13 +82,19 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
68
82
  guard let self else { return }
69
83
  switch type {
70
84
  case .writeNewCommentPressed(let actionType):
85
+ self.emitAction(type: "writeNewCommentPressed", payload: ["actionType": self.stringForActionType(actionType)])
71
86
  self.presentNewCommentViewController(actionType: actionType)
72
87
  case .trendingArticlePressed(let metadata, let containerId):
88
+ self.emitAction(type: "trendingArticlePressed", payload: ["containerId": containerId, "articleUrl": metadata.url.absoluteString])
73
89
  self.onArticlePressed(["containerId": containerId, "articleUrl": metadata.url.absoluteString])
74
90
  case .openProfilePressed(let userUUID, let presentationType):
75
91
  // Emit event and present profile for parity with Android
76
- self.onOpenProfile(["userUUID": userUUID.uuidString, "presentationType": String(describing: presentationType)])
92
+ let presentation = self.stringForPresentationType(presentationType)
93
+ self.emitAction(type: "openProfilePressed", payload: ["userUUID": userUUID.uuidString, "presentationType": presentation])
94
+ self.onOpenProfile(["userUUID": userUUID.uuidString, "presentationType": presentation])
77
95
  self.presentProfileViewController(userUUID: userUUID, presentationType: presentationType)
96
+ case .seeMoreCommentsPressed:
97
+ self.emitAction(type: "seeMoreCommentsPressed")
78
98
  default:
79
99
  break
80
100
  }
@@ -107,7 +127,22 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
107
127
  switch type {
108
128
  case .trendingArticlePressed(let metadata, let containerId):
109
129
  profileVC.dismiss(animated: true)
130
+ self.emitAction(type: "trendingArticlePressed", payload: ["containerId": containerId, "articleUrl": metadata.url.absoluteString])
110
131
  self.onArticlePressed(["containerId": containerId, "articleUrl": metadata.url.absoluteString])
132
+ case .notificationPressed(let presentation):
133
+ var payload: [String: Any] = [:]
134
+ switch presentation {
135
+ case .profile(let userUUID):
136
+ payload["presentationType"] = "profile"
137
+ payload["userUUID"] = userUUID.uuidString
138
+ case .content(let containerUUID, let contentUUID, let containerId, let articleMetadata):
139
+ payload["presentationType"] = "content"
140
+ payload["containerUUID"] = containerUUID.uuidString
141
+ payload["contentUUID"] = contentUUID.uuidString
142
+ payload["containerId"] = containerId
143
+ payload["articleUrl"] = articleMetadata.url.absoluteString
144
+ }
145
+ self.emitAction(type: "notificationPressed", payload: payload)
111
146
  default: break
112
147
  }
113
148
  }
@@ -129,8 +164,11 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
129
164
  let callbacks: VFActionsCallbacks = { [weak self] type in
130
165
  guard let self else { return }
131
166
  switch type {
132
- case .commentPosted:
133
- self.onNewComment([:])
167
+ case .commentPosted(let contentUUID):
168
+ self.emitAction(type: "commentPosted", payload: ["content": contentUUID.uuidString])
169
+ self.onNewComment(["content": contentUUID.uuidString])
170
+ case .replyPosted(let contentUUID):
171
+ self.emitAction(type: "replyPosted", payload: ["content": contentUUID.uuidString])
134
172
  default: break
135
173
  }
136
174
  }
@@ -139,6 +177,74 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
139
177
  parentVC.present(newCommentVC, animated: true)
140
178
  }
141
179
 
180
+ private func emitAction(type: String, payload: [String: Any] = [:]) {
181
+ var event = payload
182
+ event["type"] = type
183
+ onAction(event)
184
+ }
185
+
186
+ private func resolveTheme() -> VFTheme {
187
+ switch theme?.lowercased() {
188
+ case "dark":
189
+ return .dark
190
+ case "light":
191
+ return .light
192
+ default:
193
+ return darkMode ? .dark : .light
194
+ }
195
+ }
196
+
197
+ private func applyThemeIfReady() {
198
+ previewCommentsViewController?.setTheme(theme: resolveTheme())
199
+ }
200
+
201
+ private func resolveColor(key: String, fallbackKey: String, fallback: UIColor) -> UIColor {
202
+ let hex = (colors[key] as? String) ?? (colors[fallbackKey] as? String)
203
+ if let hex, let parsed = UIColor.vfColor(fromHex: hex) {
204
+ return parsed
205
+ }
206
+ return fallback
207
+ }
208
+
209
+ private func resolveAvatarColors() -> [UIColor]? {
210
+ let raw = colors["colorAvatars"] ?? colors["avatars"]
211
+ let list: [String]? = (raw as? [String]) ?? (raw as? [Any])?.compactMap { $0 as? String }
212
+ guard let list, list.count == Constants.AvatarColors.colors.count else {
213
+ return nil
214
+ }
215
+ var parsed: [UIColor] = []
216
+ parsed.reserveCapacity(list.count)
217
+ for hex in list {
218
+ guard let color = UIColor.vfColor(fromHex: hex) else { return nil }
219
+ parsed.append(color)
220
+ }
221
+ return parsed
222
+ }
223
+
224
+ private func stringForActionType(_ actionType: VFNewCommentActionType) -> String {
225
+ switch actionType {
226
+ case .create:
227
+ return "create"
228
+ case .edit:
229
+ return "edit"
230
+ case .reply:
231
+ return "reply"
232
+ @unknown default:
233
+ return "create"
234
+ }
235
+ }
236
+
237
+ private func stringForPresentationType(_ presentationType: VFProfilePresentationType) -> String {
238
+ switch presentationType {
239
+ case .profile:
240
+ return "profile"
241
+ case .feed:
242
+ return "feed"
243
+ @unknown default:
244
+ return "profile"
245
+ }
246
+ }
247
+
142
248
  // MARK: VFLayoutDelegate
143
249
  func containerHeightUpdated(viewController: VFUIViewController, height: CGFloat) {
144
250
  onHeightChanged(["newHeight": height, "containerId": containerId])
@@ -146,6 +252,7 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
146
252
 
147
253
  // MARK: VFLoginDelegate
148
254
  func startLogin() {
255
+ emitAction(type: "authPressed", payload: ["requireLogin": true])
149
256
  onAuthNeeded(["requireLogin": true])
150
257
  }
151
258
 
@@ -154,6 +261,26 @@ class RNPreviewComments: ExpoView, VFLoginDelegate, VFLayoutDelegate, VFAdDelega
154
261
  func generateAd(viewController: VFUIViewController, adPosition: Int) -> VFAdView? { VFAdView() }
155
262
  }
156
263
 
264
+ extension UIColor {
265
+ static func vfColor(fromHex hex: String) -> UIColor? {
266
+ var value = hex.trimmingCharacters(in: .whitespacesAndNewlines)
267
+ if value.hasPrefix("#") {
268
+ value.removeFirst()
269
+ }
270
+ if value.count == 6 {
271
+ value = "FF" + value
272
+ }
273
+ guard value.count == 8, let hexNumber = UInt64(value, radix: 16) else {
274
+ return nil
275
+ }
276
+ let alpha = CGFloat((hexNumber & 0xFF000000) >> 24) / 255.0
277
+ let red = CGFloat((hexNumber & 0x00FF0000) >> 16) / 255.0
278
+ let green = CGFloat((hexNumber & 0x0000FF00) >> 8) / 255.0
279
+ let blue = CGFloat(hexNumber & 0x000000FF) / 255.0
280
+ return UIColor(red: red, green: green, blue: blue, alpha: alpha)
281
+ }
282
+ }
283
+
157
284
  extension UIView {
158
285
  var parentViewController: UIViewController? {
159
286
  var parentResponder: UIResponder? = self
@@ -187,9 +314,11 @@ public class PreviewCommentsModule: Module {
187
314
  Prop("articleThumbnailUrl") { (view: RNPreviewComments, v: String) in view.articleThumbnailUrl = v }
188
315
  Prop("syndicationKey") { (view: RNPreviewComments, v: String?) in view.syndicationKey = v ?? "" }
189
316
  Prop("darkMode") { (view: RNPreviewComments, v: Bool?) in view.darkMode = v ?? false }
317
+ Prop("theme") { (view: RNPreviewComments, v: String?) in view.theme = v }
318
+ Prop("colors") { (view: RNPreviewComments, v: [String: Any]?) in view.colors = v ?? [:] }
190
319
 
191
320
  // Events
192
- Events("onHeightChanged", "onAuthNeeded", "onOpenProfile", "onNewComment", "onArticlePressed")
321
+ Events("onHeightChanged", "onAuthNeeded", "onOpenProfile", "onNewComment", "onArticlePressed", "onAction")
193
322
  }
194
323
  }
195
324
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viafoura/sdk-react-native",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Viafoura React Native SDK",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",