From b851fafff4eb2e8421bb4de53e2edab6a96fec11 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:03:10 -0400 Subject: [PATCH 01/15] chore: refactor to follow Expo's guidance This isn't a change in functionality, but it matches what Expo is doing with their Compose components. --- .../reactnativesdk/RNBibleReaderViewModule.kt | 5 ++++- .../reactnativesdk/RNBibleTextViewModule.kt | 14 ++++++++++++-- .../reactnativesdk/RNBibleWidgetViewModule.kt | 5 ++++- .../RNSignInWithYouVersionButtonModule.kt | 16 ++++++++++++++-- .../reactnativesdk/RNVotdViewModule.kt | 17 ++++++++++++++++- 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/RNBibleReaderViewModule.kt b/android/src/main/java/com/youversion/reactnativesdk/RNBibleReaderViewModule.kt index 2c7d68e..ee12b2f 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/RNBibleReaderViewModule.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/RNBibleReaderViewModule.kt @@ -1,5 +1,6 @@ package com.youversion.reactnativesdk +import com.youversion.reactnativesdk.views.BibleReaderViewProps import com.youversion.reactnativesdk.views.YVPBibleReaderView import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition @@ -8,6 +9,8 @@ class RNBibleReaderViewModule : Module() { override fun definition() = ModuleDefinition { Name("BibleReaderView") - View(YVPBibleReaderView::class) + View("YVPBibleReaderView") { props: BibleReaderViewProps -> + YVPBibleReaderView(props) + } } } diff --git a/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt b/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt index e48070d..506ca65 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt @@ -1,15 +1,25 @@ package com.youversion.reactnativesdk +import androidx.compose.runtime.remember +import com.youversion.reactnativesdk.views.BibleTextViewProps import com.youversion.reactnativesdk.views.YVPBibleTextView import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition +import expo.modules.kotlin.viewevent.getValue class RNBibleTextViewModule : Module() { override fun definition() = ModuleDefinition { Name("BibleTextView") - View(YVPBibleTextView::class) { - Events("onTap") + View( + name = "YVPBibleTextView", + events = { Events("onTap") } + ) { props: BibleTextViewProps -> + val onTap by remember { EventDispatcher() } + + YVPBibleTextView(props) { + onTap(Unit) + } } } } diff --git a/android/src/main/java/com/youversion/reactnativesdk/RNBibleWidgetViewModule.kt b/android/src/main/java/com/youversion/reactnativesdk/RNBibleWidgetViewModule.kt index 598f985..7014e59 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/RNBibleWidgetViewModule.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/RNBibleWidgetViewModule.kt @@ -1,5 +1,6 @@ package com.youversion.reactnativesdk +import com.youversion.reactnativesdk.views.BibleWidgetViewProps import com.youversion.reactnativesdk.views.YVPBibleWidgetView import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition @@ -8,6 +9,8 @@ class RNBibleWidgetViewModule : Module() { override fun definition() = ModuleDefinition { Name("BibleWidgetView") - View(YVPBibleWidgetView::class) + View("YVPBibleWidgetView") { props: BibleWidgetViewProps -> + YVPBibleWidgetView(props) + } } } diff --git a/android/src/main/java/com/youversion/reactnativesdk/RNSignInWithYouVersionButtonModule.kt b/android/src/main/java/com/youversion/reactnativesdk/RNSignInWithYouVersionButtonModule.kt index 9a5730b..bc1611e 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/RNSignInWithYouVersionButtonModule.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/RNSignInWithYouVersionButtonModule.kt @@ -1,15 +1,27 @@ package com.youversion.reactnativesdk +import androidx.compose.runtime.remember +import com.youversion.reactnativesdk.views.SignInWithYouVersionButtonProps import com.youversion.reactnativesdk.views.YVPSignInWithYouVersionButton import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition +import expo.modules.kotlin.viewevent.getValue class RNSignInWithYouVersionButtonModule : Module() { override fun definition() = ModuleDefinition { Name("SignInWithYouVersionButton") - View(YVPSignInWithYouVersionButton::class) { - Events("onTap") + View( + name = "YVPSignInWithYouVersionButton", + events = { Events("onTap") } + ) { props: SignInWithYouVersionButtonProps -> + val onTap by remember { EventDispatcher() } + + YVPSignInWithYouVersionButton( + props = props + ) { + onTap(Unit) + } } } } \ No newline at end of file diff --git a/android/src/main/java/com/youversion/reactnativesdk/RNVotdViewModule.kt b/android/src/main/java/com/youversion/reactnativesdk/RNVotdViewModule.kt index 34953d6..e771844 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/RNVotdViewModule.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/RNVotdViewModule.kt @@ -1,13 +1,28 @@ package com.youversion.reactnativesdk +import androidx.compose.runtime.remember +import com.youversion.reactnativesdk.views.VotdViewProps import com.youversion.reactnativesdk.views.YVPVotdView import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition +import expo.modules.kotlin.viewevent.getValue class RNVotdViewModule : Module() { override fun definition() = ModuleDefinition { Name("VotdView") - View(YVPVotdView::class) + View( + name = "YVPVotdView", + events = { Events("onSharePress", "onFullChapterPress") } + ) { props: VotdViewProps -> + val onSharePress by remember { EventDispatcher() } + val onFullChapterPress by remember { EventDispatcher() } + + YVPVotdView( + props = props, + onSharePress = { onSharePress(Unit) }, + onFullChapterPress = { onFullChapterPress(Unit) } + ) + } } } From ff51b2a8662b40e70e3f9fe4b5eb8086567a1134 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:03:29 -0400 Subject: [PATCH 02/15] chore: add icon assets for bottom nav --- example/assets/profile.png | Bin 0 -> 1285 bytes example/assets/reader.png | Bin 0 -> 1051 bytes example/assets/votd.png | Bin 0 -> 1778 bytes example/assets/widget.png | Bin 0 -> 974 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 example/assets/profile.png create mode 100644 example/assets/reader.png create mode 100644 example/assets/votd.png create mode 100644 example/assets/widget.png diff --git a/example/assets/profile.png b/example/assets/profile.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0fd2bf423eb75a7d7c0ddc1e03c6ff8539d88e GIT binary patch literal 1285 zcmV+g1^W7lP){!0sTfz;Xp~2UZF0K<>ak<`hvCIJcZEWCr{I6n~%u zGI0;TB;Gx!dJ;2NUwxo<-Aq4jtAU{Ri$d_vg=B}@_kI)}70U5sh1Z?ZXsEwnNi zVSnJV!pVPbWooBk5W)bw>CD{W5F$ohia`h?k%Cm3l=5M_-O8;av>C_j%Fa$S;g(is zR6M1CA{DmV2e4gFB-T_9-HAAG@noJD6R~#NdP_ii?^&DkoGk zCbBW}6=Zi+3dFjr^FPKGoG`v#(&EBwkU8Y|NLrJeQkP2`W}A(RkPwrS-=>3KafatRb4ZOF4^EE$nd+!U;Yg{m=%L+uAfPKgkD# zNSelvu@6F+fB<+2(pS{`201_+qT`^a@W1tUxIma;v zR|h4WqXmgulA(au0&<(l6>Svyiog8u}F^&SZfzIecLSmJ3ZxwIZyItX z%xOA%2kde!#yvN`!UMr7tJk*L`$Zg>HVY9X2he0Ol}vHt9Kys zBJ4h>dc^KXedMqU%IJQ`ya-!pTA0`{C>5`jQia5hZtZ0sYq#@iZE?f6V+Vx2mLuCU z?{&QhYfdr;8&vBZ3;#k;`W3p?=0^egWglzL#~aSOZaX2B#Q-duYIcxHgC9UI!X{pTLj_~sm@X$*?-8dr;bTA_h|$t9$<4vq3a!arI0ymNi!i_Ua+yh) z7yjd)XUsjT=KY-7!Kbr%5$5;aKP^8}be(MB;6)gIC(|avC$p6eM5Y~XSeTPBPdj`v vTiHP54*&oF|NqGRDQ*A&00v1!K~w_(vF^r~LmSBy00000NkvXXu0mjftMW?M literal 0 HcmV?d00001 diff --git a/example/assets/reader.png b/example/assets/reader.png new file mode 100644 index 0000000000000000000000000000000000000000..89d8d97ad99ec2870deff0f0c6e0d2cd2a0a80af GIT binary patch literal 1051 zcmV+$1mydPP)i`$Is3kJP3S+>juw|3evn^uqV1M$knwBZ@?A&G{Ba@<{6>p=k*p0d34F|c61D$~P{ug!y zjR+W4@YPI`@d0k_bmF{lI-VmbWC5v-LSeX16HSzPoTBT@3vfR z8d)7Ml}P9`cDbbnzRtR*L9UTY4Sbz-PXl8vq{b!@lp9Zk#vwIuoOi9;2fRsmfPcA~ za{{Es<~&43Zk!^6pg8-~j*3TRwVANxi)-(a^ zCA{5hn&yq`Lcf6p0JnYN<9S=Q+ah3@RKApq=T86vu>9Nir{sn7{vzwmJxHC4d34M!vB4#GW8C7np=UTUwJuj zB-N*=U|m=Va^NaS<1R3uB~uQ(q_l}Y|Dj*5oc#jF&D9>f{Q_%AFzeQ+JIN&T8P)G$m;JwNz#g2uy~^o21#_|OW*-1R=^?0q6& z_-^O9pMa^cF#`az_=uBY9CAc^_BNb03GH>D#xE0RVn75O=2>e&u$K=bIXn24uLTYe z;QG+bJcEwK+A;y7VKFd#nf2bbN6@zMc=I>kb|ibV?i=i^4Yf%#ih+AU5D#Bm}kSkOMd)cX3C?rPvA!P@E3L0vXWU6N`J}P z<*u!>T++RSjWOF48J9RU*rI2w-EOCMkr?xsjNcMM6*)PzVi3qbKR;iHDpmIqrXvBe zZj%B)+u+Q)WG;~J!WhHIB20&_fJq>9oJqQJbYZKf&n8UABCuORuAtgU7a$toEI5VS z!0rWNgSFW|;2`S|YB^M5jD``tjA_PmZ);?HkKVvNZ zbMO$l^H+?~h60~8Y%0ID*vcy9uv9F2J8CdthO8$dARb5@69M=aka>`PYYouU$F~kX zPw)*R994?3nS&&~MycKNE_M@jZ#{}IqY7yNxga3wD43zs063mW4mG6%GGidtxi~S0 zKG1FzNp2Y%QjaFgBuoq-w`@}YkZ)s&Sq+mGD%vL{*kHEr7*p(!Ys;L{A5%{v947>@ za{w&f#^N#G)=F!Ag;_c#Xo}YQSVQN>qf(b`#zgGSL#?##Z_P#+!0uA%KoH%sM*oCF#h*-$dk^ zytF41j!+fNw*6QPW;PJxV`=JdV%ZNQT9~@XBmJt^#4W$}6 zRTvDFs$VfBnc=!kqMr(xFrQ6rRSdcjHVlTUF>E8vK*EiXd%3$36Z@t1T$YjHbs4LY zxAic~jV^@uJRqEW&{rDox21fj^y9#=xo^4kje{)q8QA9E&lMNs{2D9J5BRD$OTE52 z03Z0c8|4UNuk0aPCy&P0M(W2(ACB?w&$z=j4vI_Ga|mMbu&IZt9Ev7^v(y{GzgJ3) z`v4#aj4_qFE&6gPie`A7ijm5ho59U+u5qp%W~TPKnd@2F%@L!FR<ay!Qa9^g z)+aK$_SZGX@Jj2pfcFp!UnBM>-|Mi|TOD}c4V>=!64zDwJG>^Bmmp#KE9bOtfg zsLhy&eeY~$C?*jmXiU;pZ~)jA0J~=+A|rmT=3=g_Wu<=B`iwE%I11)sPBcgV4#s*) zV$9HnKqL+T-eXT!X*n{gMn^p2x^UYw#$U!1&w1uxE?P@&wuj0n!mFcIpy1OH2moFC zo-YX)v+W-vj)!e-ac}BNXQLE+&fx10LFF&R=J)Q6@A+~p>A{3uq5#gP_9aOBz398H z#BB3Jo(2K%I5JFXp;Sdq~lM7S#fNp;8uK)l507*qoM6N<$f&(i~s{jB1 literal 0 HcmV?d00001 diff --git a/example/assets/widget.png b/example/assets/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..ae4c0d0ce5d8a1f18f106ddfa716bf7ac66384c5 GIT binary patch literal 974 zcmV;<12O!GP)@46-lB`B!)PRd1ZV~!2f5B85Wh3u3)rK`Uz-2y z?+N_q8~)tkwH?-4ziO>Jtxu3NFP+lkPH1l$v!-E7KYNGI-_GAP2m@z7A;F~}F)w#$ z=ggt9+iiQvk1ZBYY~b#1_iOY{LdUwK;L^EevN4$63z`LdIiFuiPk&1Y?FZ!NXqu?^ zfMx-IbR8Yzrs*OqC^XV1Zgz~7hGqe~f(I?e7-b44*Nd>c5zEU z9|jn|J0Q~#|1ErO$^!<+xejyZ;dcjRx_=(XGQ|gWA<=P`zhaYeq4~fSK+aby2bvE& z_f|<=2;CeD7(Z5+dCM(;pP5x+{33#cVazWI%{;=u4??*d7g*07(NO#EfwWsOg| zuEl}Z1eWf{Qy`!|JTNXGQe$Z~dko-J%wa%N{sjQU&}bZnzF>S#w10LSo>ppGsayVJ zBlsL{@qM2oy?{|m&2H0T{Bn41%YA@d7*ZN&q8_*q+O0q$IcpSn_|-GRee!mTNxu|DL+EH=V#FAhA9!)-7g zw__~!Cr%^9>qSFBb40V_rNBD*bcS{jK5eXHnql#PfRAKob~HO>-zLRYF6M%5(BT;tG@dQ wT>Tbp6!;GS0RR6#^mBUv000I_L_t&o0CJB%m#c_#9{>OV07*qoM6N<$f>iXpnE(I) literal 0 HcmV?d00001 From de840a3c7497957ef5668f5ffd1d11b0ee41b518 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:04:16 -0400 Subject: [PATCH 03/15] chore: use icons in bottom navigation --- example/App.tsx | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/example/App.tsx b/example/App.tsx index d970826..da56ec8 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -2,6 +2,7 @@ import { createNativeBottomTabNavigator } from "@react-navigation/bottom-tabs/un import { NavigationContainer } from "@react-navigation/native"; import { YouVersionPlatform } from "@youversion/platform-sdk-reactnative"; import { useEffect } from "react"; +import { Platform } from "react-native"; import { ProfileScreen } from "./src/screens/ProfileScreen"; import { ReaderScreen } from "./src/screens/ReaderScreen"; @@ -24,7 +25,13 @@ export default function App() { component={ReaderScreen} options={{ tabBarLabel: "Bible", - tabBarIcon: { type: "sfSymbol", name: "book.closed.fill" }, + tabBarIcon: + Platform.OS === "ios" + ? { type: "sfSymbol", name: "book.fill" } + : { + type: "image", + source: require("./assets/reader.png"), + }, }} /> From 6f94d0638537f24502a7383a5c4c20ef6b2f6e73 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:04:51 -0400 Subject: [PATCH 04/15] chore: refactor VOTD Compose to be functional component --- .../reactnativesdk/views/YVPVotdView.kt | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt index eed24a9..28d9fac 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt @@ -1,50 +1,48 @@ package com.youversion.reactnativesdk.views -import android.content.Context import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import expo.modules.kotlin.AppContext -import expo.modules.kotlin.views.ComposableScope +import com.youversion.platform.ui.views.votd.CompactVerseOfTheDay +import com.youversion.platform.ui.views.votd.VerseOfTheDay import expo.modules.kotlin.views.ComposeProps -import expo.modules.kotlin.views.ExpoComposeView data class VotdViewProps( val bibleVersionId: MutableState = mutableStateOf(3034), val colorScheme: MutableState = mutableStateOf(null), + val showIcon: MutableState = mutableStateOf(null), + val isCompact: MutableState = mutableStateOf(null) ) : ComposeProps -class YVPVotdView(context: Context, appContext: AppContext) : - ExpoComposeView(context, appContext, withHostingView = true) { - - override val props = VotdViewProps() - - @Composable - override fun ComposableScope.Content() { - val isDark = when (props.colorScheme.value) { - "dark" -> true - "light" -> false - else -> isSystemInDarkTheme() - } +@Composable +fun YVPVotdView(props: VotdViewProps, onSharePress: () -> Unit, onFullChapterPress: () -> Unit) { + if (props.isCompact.value == true) { + CompactVerseOfTheDay( + bibleVersionId = props.bibleVersionId.value ?: 111, + dark = isDark(props), + showIcon = isIconVisible(props) + ) + } else { + VerseOfTheDay( + bibleVersionId = props.bibleVersionId.value ?: 111, + dark = isDark(props), + onShareClick = { onSharePress() }, + onFullChapterClick = { onFullChapterPress() }, + showIcon = isIconVisible(props) + ) + } +} - // TODO: Replace with actual VerseOfTheDay composable when Kotlin SDK is ready - Box( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Text( - text = "VotdView placeholder - versionId: ${props.bibleVersionId.value}", - color = if (isDark) Color.White else Color.DarkGray - ) - } +@Composable +fun isDark(props: VotdViewProps): Boolean { + return when (props.colorScheme.value) { + "dark" -> true + "light" -> false + else -> isSystemInDarkTheme() } } + +fun isIconVisible(props: VotdViewProps): Boolean { + return props.showIcon.value ?: true +} From c8b2ef81e70ba52120e696e22804f23bb5bbf80c Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:05:09 -0400 Subject: [PATCH 05/15] chore: refactor BibleReaderView to be functional component --- .../views/YVPBibleReaderView.kt | 66 ++++++++----------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt index fd20704..635f0fd 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt @@ -1,16 +1,11 @@ package com.youversion.reactnativesdk.views -import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier import com.youversion.platform.core.bibles.domain.BibleReference import com.youversion.platform.reader.BibleReader -import expo.modules.kotlin.AppContext -import expo.modules.kotlin.views.ComposableScope import expo.modules.kotlin.views.ComposeProps -import expo.modules.kotlin.views.ExpoComposeView const val DEFAULT_BEREAN_STANDARD_BIBLE_VERSION = 3034 @@ -26,42 +21,37 @@ data class BibleReaderViewProps( val hasReference: MutableState = mutableStateOf(null) ) : ComposeProps -class YVPBibleReaderView(context: Context, appContext: AppContext) : - ExpoComposeView(context, appContext, withHostingView = true) { - override val props = BibleReaderViewProps() +@Composable +fun YVPBibleReaderView(props: BibleReaderViewProps) { + BibleReader( + appName = props.appName.value ?: "", + appSignInMessage = props.signInMessage.value ?: "", + bibleReference = bibleReference(props), + ) +} - @Composable - override fun ComposableScope.Content() { - BibleReader( - appName = props.appName.value ?: "", - appSignInMessage = props.signInMessage.value ?: "", - bibleReference = bibleReference(), +fun bibleReference(props: BibleReaderViewProps): BibleReference? { + val start = props.verseStart.value + val end = props.verseEnd.value + + if (start != null && end != null) { + return BibleReference( + versionId = props.versionId.value ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, + bookUSFM = props.bookUSFM.value ?: "JHN", + chapter = props.chapter.value ?: 1, + verseStart = start, + verseEnd = end ) } - fun bibleReference(): BibleReference? { - val start = props.verseStart.value - val end = props.verseEnd.value - - if (start != null && end != null) { - return BibleReference( - versionId = props.versionId.value ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, - bookUSFM = props.bookUSFM.value ?: "JHN", - chapter = props.chapter.value ?: 1, - verseStart = start, - verseEnd = end - ) - } - - if (props.hasReference.value == true) { - return BibleReference( - versionId = props.versionId.value ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, - bookUSFM = props.bookUSFM.value ?: "JHN", - chapter = props.chapter.value ?: 1, - verse = props.verse.value - ) - } - - return null + if (props.hasReference.value == true) { + return BibleReference( + versionId = props.versionId.value ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, + bookUSFM = props.bookUSFM.value ?: "JHN", + chapter = props.chapter.value ?: 1, + verse = props.verse.value + ) } + + return null } From 0db57951e7051dc208def0b71b84251b1cca377a Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:06:32 -0400 Subject: [PATCH 06/15] feat: implement BibleTextView as functional component --- .../reactnativesdk/views/YVPBibleTextView.kt | 134 +++++++++++++----- 1 file changed, 96 insertions(+), 38 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt index 5cc2e26..67a8b7a 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt @@ -1,34 +1,29 @@ package com.youversion.reactnativesdk.views -import android.content.Context -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text +import android.os.Build +import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import expo.modules.kotlin.AppContext -import expo.modules.kotlin.viewevent.EventDispatcher -import expo.modules.kotlin.views.ComposableScope +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.sp +import com.youversion.platform.core.bibles.domain.BibleReference +import com.youversion.platform.ui.views.BibleText +import com.youversion.platform.ui.views.BibleTextFootnoteMode +import com.youversion.platform.ui.views.BibleTextOptions import expo.modules.kotlin.views.ComposeProps -import expo.modules.kotlin.views.ExpoComposeView data class BibleTextViewProps( - // Styling - val fontFamily: MutableState = mutableStateOf(null), - val fontSize: MutableState = mutableStateOf(16f), + val fontFamily: MutableState = mutableStateOf("Times New Roman"), + val fontSize: MutableState = mutableStateOf(16f), val lineSpacing: MutableState = mutableStateOf(null), val paragraphSpacing: MutableState = mutableStateOf(null), - val textColor: MutableState = mutableStateOf(null), - val wocColor: MutableState = mutableStateOf(null), - val footnoteMode: MutableState = mutableStateOf("none"), + val textColor: MutableState = mutableStateOf(null), + val wocColor: MutableState = mutableStateOf(null), + val footnoteMode: MutableState = mutableStateOf(null), val renderVerseNumbers: MutableState = mutableStateOf(true), - // Bible reference val versionId: MutableState = mutableStateOf(null), val bookUSFM: MutableState = mutableStateOf(null), val chapter: MutableState = mutableStateOf(null), @@ -37,25 +32,88 @@ data class BibleTextViewProps( val verseEnd: MutableState = mutableStateOf(null), ) : ComposeProps -class YVPBibleTextView(context: Context, appContext: AppContext) : - ExpoComposeView(context, appContext, withHostingView = true) { - - override val props = BibleTextViewProps() - private val onTap by EventDispatcher() - - @Composable - override fun ComposableScope.Content() { - // TODO: Replace with actual BibleText composable when Kotlin SDK is ready - Box( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Text( - text = "BibleTextView placeholder - versionId: ${props.versionId.value}, " + - "book: ${props.bookUSFM.value}, chapter: ${props.chapter.value}", - color = Color.Gray - ) - } +val defaultTextOptions = BibleTextOptions() + +@Composable +fun YVPBibleTextView(props: BibleTextViewProps, onTap: () -> Unit) { + BibleText( + reference = bibleReference(props), + textOptions = textOptions(props) + ) +} + +fun bibleReference(props: BibleTextViewProps): BibleReference { + if (props.chapter.value == null) { + throw IllegalStateException("Chapter is required") } + + if (props.bookUSFM.value == null) { + throw IllegalStateException("Book is required") + } + + if (props.versionId.value == null) { + throw IllegalStateException("Version is required") + } + + val start = props.verseStart.value + val end = props.verseEnd.value + + if (start != null && end != null) { + return BibleReference( + versionId = props.versionId.value!!, + bookUSFM = props.bookUSFM.value!!, + chapter = props.chapter.value!!, + verseStart = start, + verseEnd = end + ) + } + + return BibleReference( + versionId = props.versionId.value!!, + bookUSFM = props.bookUSFM.value!!, + chapter = props.chapter.value!!, + verse = props.verse.value + ) } + +fun textOptions(props: BibleTextViewProps): BibleTextOptions { + return BibleTextOptions( + fontFamily = FontFamily.Serif, + fontSize = props.fontSize.value.sp, + lineSpacing = props.lineSpacing.value?.sp ?: defaultTextOptions.lineSpacing, + textColor = composeColor(props.textColor.value), + wocColor = composeColor(props.wocColor.value) ?: composeColor(0xFFF04C59), + renderVerseNumbers = props.renderVerseNumbers.value + ?: defaultTextOptions.renderVerseNumbers, + footnoteMode = footnodeMode(props) + ) +} + +@RequiresApi(Build.VERSION_CODES.O) +fun composeColor(androidColor: Color?): Color? { + if (androidColor == null) { + return null + } + + return Color( + alpha = androidColor.alpha, + red = androidColor.red, + green = androidColor.green, + blue = androidColor.blue + ) +} + +fun composeColor(hexColor: Long): Color { + return Color(hexColor) +} + +fun footnodeMode(props: BibleTextViewProps): BibleTextFootnoteMode { + return when (props.footnoteMode.value) { + "none" -> BibleTextFootnoteMode.NONE + "inline" -> BibleTextFootnoteMode.INLINE + "marker" -> BibleTextFootnoteMode.MARKER + "letters" -> BibleTextFootnoteMode.LETTERS + "image" -> BibleTextFootnoteMode.IMAGE + else -> defaultTextOptions.footnoteMode + } +} \ No newline at end of file From 5841a7ab4adca4a2839c56f5671226f606c251df Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:06:56 -0400 Subject: [PATCH 07/15] feat: implement BibleWidgetView as functional component --- .../views/YVPBibleWidgetView.kt | 97 ++++++++++++------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt index ad13ec6..52b2e81 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt @@ -1,22 +1,16 @@ package com.youversion.reactnativesdk.views -import android.content.Context import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import expo.modules.kotlin.AppContext -import expo.modules.kotlin.views.ComposableScope +import com.youversion.platform.core.bibles.domain.BibleReference +import com.youversion.platform.ui.views.BibleTextOptions +import com.youversion.platform.ui.views.card.BibleCard import expo.modules.kotlin.views.ComposeProps -import expo.modules.kotlin.views.ExpoComposeView data class BibleWidgetViewProps( // Bible reference @@ -31,31 +25,62 @@ data class BibleWidgetViewProps( val colorScheme: MutableState = mutableStateOf(null), ) : ComposeProps -class YVPBibleWidgetView(context: Context, appContext: AppContext) : - ExpoComposeView(context, appContext, withHostingView = true) { - - override val props = BibleWidgetViewProps() - - @Composable - override fun ComposableScope.Content() { - val isDark = when (props.colorScheme.value) { - "dark" -> true - "light" -> false - else -> isSystemInDarkTheme() - } - - // TODO: Replace with actual BibleWidget composable when Kotlin SDK is ready - Box( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Text( - text = "BibleWidgetView placeholder\n" + - "${props.bookUSFM.value} ${props.chapter.value}:${props.verse.value ?: "${props.verseStart.value}-${props.verseEnd.value}"}", - color = if (isDark) Color.White else Color.DarkGray, - fontSize = (props.fontSize.value ?: 23f).sp - ) - } +@Composable +fun YVPBibleWidgetView(props: BibleWidgetViewProps) { + BibleCard( + reference = bibleReference(props), + textOptions = textOptions(props), + modifier = Modifier.fillMaxHeight(), + ) +} + +fun textOptions(props: BibleWidgetViewProps): BibleTextOptions { + if (props.fontSize.value == null) { + return BibleTextOptions() + } + + return BibleTextOptions(fontSize = props.fontSize.value!!.sp) +} + +fun bibleReference(props: BibleWidgetViewProps): BibleReference { + if (props.chapter.value == null) { + throw IllegalStateException("Chapter is required") + } + + if (props.bookUSFM.value == null) { + throw IllegalStateException("Book is required") } + + if (props.versionId.value == null) { + throw IllegalStateException("Version is required") + } + + val start = props.verseStart.value + val end = props.verseEnd.value + + if (start != null && end != null) { + return BibleReference( + versionId = props.versionId.value!!, + bookUSFM = props.bookUSFM.value!!, + chapter = props.chapter.value!!, + verseStart = start, + verseEnd = end + ) + } + + return BibleReference( + versionId = props.versionId.value!!, + bookUSFM = props.bookUSFM.value!!, + chapter = props.chapter.value!!, + verse = props.verse.value + ) } + +@Composable +fun isDark(props: BibleWidgetViewProps): Boolean { + return when (props.colorScheme.value) { + "dark" -> true + "light" -> false + else -> isSystemInDarkTheme() + } +} \ No newline at end of file From 1703776b2179d81a6c16caa3fe3af57a977bded6 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:07:16 -0400 Subject: [PATCH 08/15] chore: refactor sign in button as functional component --- .../views/YVPSignInWithYouVersionButton.kt | 78 +++++++++---------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt index 19ceabe..bf4aa8d 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt @@ -1,6 +1,5 @@ package com.youversion.reactnativesdk.views -import android.content.Context import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -9,10 +8,7 @@ import androidx.compose.ui.graphics.Shape import com.youversion.platform.ui.views.SignInWithYouVersionButton import com.youversion.platform.ui.views.SignInWithYouVersionButtonDefaults import com.youversion.platform.ui.views.SignInWithYouVersionButtonMode -import expo.modules.kotlin.AppContext -import expo.modules.kotlin.views.ComposableScope import expo.modules.kotlin.views.ComposeProps -import expo.modules.kotlin.views.ExpoComposeView data class SignInWithYouVersionButtonProps( val mode: MutableState = mutableStateOf("full"), @@ -21,50 +17,48 @@ data class SignInWithYouVersionButtonProps( val colorScheme: MutableState = mutableStateOf(null) ) : ComposeProps -class YVPSignInWithYouVersionButton(context: Context, appContext: AppContext) : - ExpoComposeView(context, appContext, withHostingView = true) { - override val props = SignInWithYouVersionButtonProps() -// private val onTap by EventDispatcher() - @Composable - override fun ComposableScope.Content() { - SignInWithYouVersionButton( - mode = mode(), - stroked = stroked(), - shape = shape(), - dark = isDark(), - permissions = { HashSet() } - ) - } +@Composable +fun YVPSignInWithYouVersionButton( + props: SignInWithYouVersionButtonProps, + onTap: () -> Unit +) { + SignInWithYouVersionButton( + mode = mode(props), + stroked = stroked(props), + shape = shape(props), + dark = isDark(props), + permissions = { HashSet() } + ) +} - fun mode(): SignInWithYouVersionButtonMode { - return when (props.mode.value) { - "full" -> SignInWithYouVersionButtonMode.FULL - "compact" -> SignInWithYouVersionButtonMode.COMPACT - "iconOnly" -> SignInWithYouVersionButtonMode.ICON_ONLY - else -> SignInWithYouVersionButtonMode.FULL - } +fun mode(props: SignInWithYouVersionButtonProps): SignInWithYouVersionButtonMode { + return when (props.mode.value) { + "full" -> SignInWithYouVersionButtonMode.FULL + "compact" -> SignInWithYouVersionButtonMode.COMPACT + "iconOnly" -> SignInWithYouVersionButtonMode.ICON_ONLY + else -> SignInWithYouVersionButtonMode.FULL } +} - fun stroked(): Boolean { - return props.isStroked.value ?: true - } +fun stroked(props: SignInWithYouVersionButtonProps): Boolean { + return props.isStroked.value ?: true +} - @Composable - fun shape(): Shape { - return when (props.shape.value) { - "capsule" -> SignInWithYouVersionButtonDefaults.capsuleShape - "rectangle" -> SignInWithYouVersionButtonDefaults.rectangleShape - else -> SignInWithYouVersionButtonDefaults.capsuleShape - } +@Composable +fun shape(props: SignInWithYouVersionButtonProps): Shape { + return when (props.shape.value) { + "capsule" -> SignInWithYouVersionButtonDefaults.capsuleShape + "rectangle" -> SignInWithYouVersionButtonDefaults.rectangleShape + else -> SignInWithYouVersionButtonDefaults.capsuleShape } +} - @Composable - fun isDark(): Boolean { - return when (props.colorScheme.value) { - "dark" -> true - "light" -> false - else -> isSystemInDarkTheme() - } +@Composable +fun isDark(props: SignInWithYouVersionButtonProps): Boolean { + return when (props.colorScheme.value) { + "dark" -> true + "light" -> false + else -> isSystemInDarkTheme() } } \ No newline at end of file From 3f53858cdfde5c740725346c2b54e6543a3509a9 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Tue, 31 Mar 2026 21:14:49 -0400 Subject: [PATCH 09/15] chore: use correct for each platform --- src/components/BibleTextView.tsx | 15 +++++++++++---- src/components/BibleWidgetView.tsx | 14 ++++++++++---- src/components/VotdView.tsx | 14 ++++++++++---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/components/BibleTextView.tsx b/src/components/BibleTextView.tsx index 42d77ea..c281e57 100644 --- a/src/components/BibleTextView.tsx +++ b/src/components/BibleTextView.tsx @@ -1,6 +1,7 @@ -import { Host } from "@expo/ui/swift-ui"; +import { Host as AndroidHost } from "@expo/ui/jetpack-compose"; +import { Host as IosHost } from "@expo/ui/swift-ui"; import { requireNativeView } from "expo"; -import { StyleProp, StyleSheet, ViewStyle } from "react-native"; +import { Platform, StyleProp, StyleSheet, ViewStyle } from "react-native"; import { BibleReference, @@ -11,6 +12,9 @@ import { const NativeView: React.ComponentType = requireNativeView("BibleTextView"); +const PlatformHost = Platform.OS === "ios" ? IosHost : AndroidHost; + +const MATCH_CONTENTS = { vertical: true, horizontal: false }; /** * A minimal text view for displaying a Bible passage. * The component supports font customizations and accepts an `onPress` handler @@ -25,14 +29,17 @@ export function BibleTextView({ ...props }: BibleTextViewProps) { return ( - + onPress?.(e.nativeEvent)} style={styles.component} /> - + ); } diff --git a/src/components/BibleWidgetView.tsx b/src/components/BibleWidgetView.tsx index 67a687f..fc48d9c 100644 --- a/src/components/BibleWidgetView.tsx +++ b/src/components/BibleWidgetView.tsx @@ -1,6 +1,7 @@ -import { Host } from "@expo/ui/swift-ui"; +import { Host as AndroidHost } from "@expo/ui/jetpack-compose"; +import { Host as IosHost } from "@expo/ui/swift-ui"; import { requireNativeView } from "expo"; -import { StyleProp, StyleSheet, ViewStyle } from "react-native"; +import { Platform, StyleProp, StyleSheet, ViewStyle } from "react-native"; import { BibleReference } from "../types"; @@ -9,6 +10,8 @@ const NativeView: React.ComponentType = const MATCH_CONTENTS = { vertical: true, horizontal: false }; +const PlatformHost = Platform.OS === "ios" ? IosHost : AndroidHost; + /** * An opinionated Bible passage display. * It displays the book, chapter and version name above the passage. Below the passage text, it displays copyright information and the YouVersion logo. @@ -20,9 +23,12 @@ export function BibleWidgetView({ ...props }: BibleWidgetViewProps) { return ( - + - + ); } diff --git a/src/components/VotdView.tsx b/src/components/VotdView.tsx index da129b4..ade25cd 100644 --- a/src/components/VotdView.tsx +++ b/src/components/VotdView.tsx @@ -1,6 +1,9 @@ -import { Host } from "@expo/ui/swift-ui"; +import { Host as AndroidHost } from "@expo/ui/jetpack-compose"; +import { Host as IosHost } from "@expo/ui/swift-ui"; import { requireNativeView } from "expo"; -import { StyleProp, StyleSheet, ViewStyle } from "react-native"; +import { Platform, StyleProp, StyleSheet, ViewStyle } from "react-native"; + +const PlatformHost = Platform.OS === "ios" ? IosHost : AndroidHost; const NativeView: React.ComponentType = requireNativeView("VotdView"); @@ -13,9 +16,12 @@ export function VotdView({ ...props }: VotdViewProps) { return ( - + - + ); } From 9222a32d8fadc5642236c7241f20208c8cf26dba Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Thu, 2 Apr 2026 21:31:50 -0400 Subject: [PATCH 10/15] chore: implement onTap callback --- .../reactnativesdk/RNBibleTextViewModule.kt | 7 ++++--- .../youversion/reactnativesdk/api/YVPRecords.kt | 17 +++++++++++++++++ .../reactnativesdk/views/YVPBibleTextView.kt | 9 +++++++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt b/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt index 506ca65..bcd4e66 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/RNBibleTextViewModule.kt @@ -1,6 +1,7 @@ package com.youversion.reactnativesdk import androidx.compose.runtime.remember +import com.youversion.reactnativesdk.api.VerseTappedEvent import com.youversion.reactnativesdk.views.BibleTextViewProps import com.youversion.reactnativesdk.views.YVPBibleTextView import expo.modules.kotlin.modules.Module @@ -15,10 +16,10 @@ class RNBibleTextViewModule : Module() { name = "YVPBibleTextView", events = { Events("onTap") } ) { props: BibleTextViewProps -> - val onTap by remember { EventDispatcher() } + val onTap by remember { EventDispatcher() } - YVPBibleTextView(props) { - onTap(Unit) + YVPBibleTextView(props) { event: VerseTappedEvent -> + onTap(event) } } } diff --git a/android/src/main/java/com/youversion/reactnativesdk/api/YVPRecords.kt b/android/src/main/java/com/youversion/reactnativesdk/api/YVPRecords.kt index c2eebe5..b4889ad 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/api/YVPRecords.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/api/YVPRecords.kt @@ -1,5 +1,6 @@ package com.youversion.reactnativesdk.api +import com.youversion.platform.core.bibles.domain.BibleReference import com.youversion.platform.core.bibles.models.BibleBook import com.youversion.platform.core.bibles.models.BibleChapter import com.youversion.platform.core.bibles.models.BibleVersion @@ -61,6 +62,20 @@ data class LanguageRecord( ) } +data class VerseTappedEvent( + @Field + val bibleReference: BibleReferenceRecord +) : Record { + constructor(bibleReference: BibleReference) : this( + bibleReference = BibleReferenceRecord( + versionId = bibleReference.versionId, + bookUSFM = bibleReference.bookUSFM, + chapter = bibleReference.chapter, + verse = bibleReference.verseStart + ) + ) +} + data class BibleVersionRecord( @Field val id: Int, @@ -155,6 +170,8 @@ data class BibleReferenceRecord( val bookUSFM: String, @Field val chapter: Int, + @Field + val verse: Int? ) : Record data class HighlightRecord( diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt index 67a8b7a..2b7ad9b 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt @@ -5,6 +5,7 @@ import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.sp @@ -12,6 +13,7 @@ import com.youversion.platform.core.bibles.domain.BibleReference import com.youversion.platform.ui.views.BibleText import com.youversion.platform.ui.views.BibleTextFootnoteMode import com.youversion.platform.ui.views.BibleTextOptions +import com.youversion.reactnativesdk.api.VerseTappedEvent import expo.modules.kotlin.views.ComposeProps data class BibleTextViewProps( @@ -35,10 +37,13 @@ data class BibleTextViewProps( val defaultTextOptions = BibleTextOptions() @Composable -fun YVPBibleTextView(props: BibleTextViewProps, onTap: () -> Unit) { +fun YVPBibleTextView(props: BibleTextViewProps, onTap: (event: VerseTappedEvent) -> Unit) { BibleText( reference = bibleReference(props), - textOptions = textOptions(props) + textOptions = textOptions(props), + onVerseTap = { reference: BibleReference, _: Offset -> + onTap(VerseTappedEvent(reference)) + } ) } From e4d75e50d68e4b5b810a4988952f7ffbbe3fe5a1 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Thu, 2 Apr 2026 21:36:36 -0400 Subject: [PATCH 11/15] chore: remove unnecessary color utility function --- .../reactnativesdk/views/YVPBibleTextView.kt | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt index 2b7ad9b..dc9d779 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt @@ -1,7 +1,5 @@ package com.youversion.reactnativesdk.views -import android.os.Build -import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf @@ -86,28 +84,14 @@ fun textOptions(props: BibleTextViewProps): BibleTextOptions { fontFamily = FontFamily.Serif, fontSize = props.fontSize.value.sp, lineSpacing = props.lineSpacing.value?.sp ?: defaultTextOptions.lineSpacing, - textColor = composeColor(props.textColor.value), - wocColor = composeColor(props.wocColor.value) ?: composeColor(0xFFF04C59), + textColor = props.textColor.value, + wocColor = props.wocColor.value ?: composeColor(0xFFF04C59), renderVerseNumbers = props.renderVerseNumbers.value ?: defaultTextOptions.renderVerseNumbers, footnoteMode = footnodeMode(props) ) } -@RequiresApi(Build.VERSION_CODES.O) -fun composeColor(androidColor: Color?): Color? { - if (androidColor == null) { - return null - } - - return Color( - alpha = androidColor.alpha, - red = androidColor.red, - green = androidColor.green, - blue = androidColor.blue - ) -} - fun composeColor(hexColor: Long): Color { return Color(hexColor) } From 95aa6389a573d9e2fe911dad98c76d8d74c4dc43 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Thu, 2 Apr 2026 21:39:33 -0400 Subject: [PATCH 12/15] chore: swap order of styles to allow for overriding --- src/components/BibleWidgetView.tsx | 2 +- src/components/VotdView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/BibleWidgetView.tsx b/src/components/BibleWidgetView.tsx index fc48d9c..20a9dd6 100644 --- a/src/components/BibleWidgetView.tsx +++ b/src/components/BibleWidgetView.tsx @@ -25,7 +25,7 @@ export function BibleWidgetView({ return ( diff --git a/src/components/VotdView.tsx b/src/components/VotdView.tsx index ade25cd..acd105d 100644 --- a/src/components/VotdView.tsx +++ b/src/components/VotdView.tsx @@ -18,7 +18,7 @@ export function VotdView({ return ( From 8ce7ac17ce739a0ce773f06b25bfedf4ea80205c Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Thu, 2 Apr 2026 21:41:17 -0400 Subject: [PATCH 13/15] chore: update default Bible version to 3034 --- .../java/com/youversion/reactnativesdk/views/YVPVotdView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt index 28d9fac..5450513 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt @@ -19,13 +19,13 @@ data class VotdViewProps( fun YVPVotdView(props: VotdViewProps, onSharePress: () -> Unit, onFullChapterPress: () -> Unit) { if (props.isCompact.value == true) { CompactVerseOfTheDay( - bibleVersionId = props.bibleVersionId.value ?: 111, + bibleVersionId = props.bibleVersionId.value ?: 3034, dark = isDark(props), showIcon = isIconVisible(props) ) } else { VerseOfTheDay( - bibleVersionId = props.bibleVersionId.value ?: 111, + bibleVersionId = props.bibleVersionId.value ?: 3034, dark = isDark(props), onShareClick = { onSharePress() }, onFullChapterClick = { onFullChapterPress() }, From 74d74e3872b8239974a8b63c913b44823d259390 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Sat, 4 Apr 2026 16:14:44 -0400 Subject: [PATCH 14/15] chore: remove mutableState It looks like Expo no longer supports piping props into mutable state. We should use the regular primitives instead. --- .../views/YVPBibleReaderView.kt | 44 ++++++------- .../reactnativesdk/views/YVPBibleTextView.kt | 66 +++++++++---------- .../views/YVPBibleWidgetView.kt | 48 +++++++------- .../views/YVPSignInWithYouVersionButton.kt | 18 +++-- .../reactnativesdk/views/YVPVotdView.kt | 20 +++--- 5 files changed, 93 insertions(+), 103 deletions(-) diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt index 635f0fd..1a5ed29 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleReaderView.kt @@ -1,8 +1,6 @@ package com.youversion.reactnativesdk.views import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import com.youversion.platform.core.bibles.domain.BibleReference import com.youversion.platform.reader.BibleReader import expo.modules.kotlin.views.ComposeProps @@ -10,46 +8,46 @@ import expo.modules.kotlin.views.ComposeProps const val DEFAULT_BEREAN_STANDARD_BIBLE_VERSION = 3034 data class BibleReaderViewProps( - val versionId: MutableState = mutableStateOf(null), - val bookUSFM: MutableState = mutableStateOf(null), - val chapter: MutableState = mutableStateOf(null), - val verse: MutableState = mutableStateOf(null), - val verseStart: MutableState = mutableStateOf(null), - val verseEnd: MutableState = mutableStateOf(null), - val appName: MutableState = mutableStateOf(null), - val signInMessage: MutableState = mutableStateOf(null), - val hasReference: MutableState = mutableStateOf(null) + val versionId: Int? = null, + val bookUSFM: String? = null, + val chapter: Int? = null, + val verse: Int? = null, + val verseStart: Int? = null, + val verseEnd: Int? = null, + val appName: String? = null, + val signInMessage: String? = null, + val hasReference: Boolean? = null ) : ComposeProps @Composable fun YVPBibleReaderView(props: BibleReaderViewProps) { BibleReader( - appName = props.appName.value ?: "", - appSignInMessage = props.signInMessage.value ?: "", + appName = props.appName ?: "", + appSignInMessage = props.signInMessage ?: "", bibleReference = bibleReference(props), ) } fun bibleReference(props: BibleReaderViewProps): BibleReference? { - val start = props.verseStart.value - val end = props.verseEnd.value + val start = props.verseStart + val end = props.verseEnd if (start != null && end != null) { return BibleReference( - versionId = props.versionId.value ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, - bookUSFM = props.bookUSFM.value ?: "JHN", - chapter = props.chapter.value ?: 1, + versionId = props.versionId ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, + bookUSFM = props.bookUSFM ?: "JHN", + chapter = props.chapter ?: 1, verseStart = start, verseEnd = end ) } - if (props.hasReference.value == true) { + if (props.hasReference == true) { return BibleReference( - versionId = props.versionId.value ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, - bookUSFM = props.bookUSFM.value ?: "JHN", - chapter = props.chapter.value ?: 1, - verse = props.verse.value + versionId = props.versionId ?: DEFAULT_BEREAN_STANDARD_BIBLE_VERSION, + bookUSFM = props.bookUSFM ?: "JHN", + chapter = props.chapter ?: 1, + verse = props.verse ) } diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt index dc9d779..878ebcc 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleTextView.kt @@ -1,8 +1,6 @@ package com.youversion.reactnativesdk.views import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontFamily @@ -15,21 +13,21 @@ import com.youversion.reactnativesdk.api.VerseTappedEvent import expo.modules.kotlin.views.ComposeProps data class BibleTextViewProps( - val fontFamily: MutableState = mutableStateOf("Times New Roman"), - val fontSize: MutableState = mutableStateOf(16f), - val lineSpacing: MutableState = mutableStateOf(null), - val paragraphSpacing: MutableState = mutableStateOf(null), - val textColor: MutableState = mutableStateOf(null), - val wocColor: MutableState = mutableStateOf(null), - val footnoteMode: MutableState = mutableStateOf(null), - val renderVerseNumbers: MutableState = mutableStateOf(true), + val fontFamily: String = "Times New Roman", + val fontSize: Float = 16f, + val lineSpacing: Float? = null, + val paragraphSpacing: Float? = null, + val textColor: Color? = null, + val wocColor: Color? = null, + val footnoteMode: String? = null, + val renderVerseNumbers: Boolean? = true, - val versionId: MutableState = mutableStateOf(null), - val bookUSFM: MutableState = mutableStateOf(null), - val chapter: MutableState = mutableStateOf(null), - val verse: MutableState = mutableStateOf(null), - val verseStart: MutableState = mutableStateOf(null), - val verseEnd: MutableState = mutableStateOf(null), + val versionId: Int? = null, + val bookUSFM: String? = null, + val chapter: Int? = null, + val verse: Int? = null, + val verseStart: Int? = null, + val verseEnd: Int? = null, ) : ComposeProps val defaultTextOptions = BibleTextOptions() @@ -46,47 +44,47 @@ fun YVPBibleTextView(props: BibleTextViewProps, onTap: (event: VerseTappedEvent) } fun bibleReference(props: BibleTextViewProps): BibleReference { - if (props.chapter.value == null) { + if (props.chapter == null) { throw IllegalStateException("Chapter is required") } - if (props.bookUSFM.value == null) { + if (props.bookUSFM == null) { throw IllegalStateException("Book is required") } - if (props.versionId.value == null) { + if (props.versionId == null) { throw IllegalStateException("Version is required") } - val start = props.verseStart.value - val end = props.verseEnd.value + val start = props.verseStart + val end = props.verseEnd if (start != null && end != null) { return BibleReference( - versionId = props.versionId.value!!, - bookUSFM = props.bookUSFM.value!!, - chapter = props.chapter.value!!, + versionId = props.versionId, + bookUSFM = props.bookUSFM, + chapter = props.chapter, verseStart = start, verseEnd = end ) } return BibleReference( - versionId = props.versionId.value!!, - bookUSFM = props.bookUSFM.value!!, - chapter = props.chapter.value!!, - verse = props.verse.value + versionId = props.versionId, + bookUSFM = props.bookUSFM, + chapter = props.chapter, + verse = props.verse ) } fun textOptions(props: BibleTextViewProps): BibleTextOptions { return BibleTextOptions( fontFamily = FontFamily.Serif, - fontSize = props.fontSize.value.sp, - lineSpacing = props.lineSpacing.value?.sp ?: defaultTextOptions.lineSpacing, - textColor = props.textColor.value, - wocColor = props.wocColor.value ?: composeColor(0xFFF04C59), - renderVerseNumbers = props.renderVerseNumbers.value + fontSize = props.fontSize.sp, + lineSpacing = props.lineSpacing?.sp ?: defaultTextOptions.lineSpacing, + textColor = props.textColor, + wocColor = props.wocColor ?: composeColor(0xFFF04C59), + renderVerseNumbers = props.renderVerseNumbers ?: defaultTextOptions.renderVerseNumbers, footnoteMode = footnodeMode(props) ) @@ -97,7 +95,7 @@ fun composeColor(hexColor: Long): Color { } fun footnodeMode(props: BibleTextViewProps): BibleTextFootnoteMode { - return when (props.footnoteMode.value) { + return when (props.footnoteMode) { "none" -> BibleTextFootnoteMode.NONE "inline" -> BibleTextFootnoteMode.INLINE "marker" -> BibleTextFootnoteMode.MARKER diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt index 52b2e81..1558c93 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPBibleWidgetView.kt @@ -3,8 +3,6 @@ package com.youversion.reactnativesdk.views import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.compose.ui.unit.sp import com.youversion.platform.core.bibles.domain.BibleReference @@ -14,15 +12,15 @@ import expo.modules.kotlin.views.ComposeProps data class BibleWidgetViewProps( // Bible reference - val versionId: MutableState = mutableStateOf(null), - val bookUSFM: MutableState = mutableStateOf(null), - val chapter: MutableState = mutableStateOf(null), - val verse: MutableState = mutableStateOf(null), - val verseStart: MutableState = mutableStateOf(null), - val verseEnd: MutableState = mutableStateOf(null), + val versionId: Int? = null, + val bookUSFM: String? = null, + val chapter: Int? = null, + val verse: Int? = null, + val verseStart: Int? = null, + val verseEnd: Int? = null, - val fontSize: MutableState = mutableStateOf(23f), - val colorScheme: MutableState = mutableStateOf(null), + val fontSize: Float? = 23f, + val colorScheme: String? = null, ) : ComposeProps @Composable @@ -35,50 +33,50 @@ fun YVPBibleWidgetView(props: BibleWidgetViewProps) { } fun textOptions(props: BibleWidgetViewProps): BibleTextOptions { - if (props.fontSize.value == null) { + if (props.fontSize == null) { return BibleTextOptions() } - return BibleTextOptions(fontSize = props.fontSize.value!!.sp) + return BibleTextOptions(fontSize = props.fontSize.sp) } fun bibleReference(props: BibleWidgetViewProps): BibleReference { - if (props.chapter.value == null) { + if (props.chapter == null) { throw IllegalStateException("Chapter is required") } - if (props.bookUSFM.value == null) { + if (props.bookUSFM == null) { throw IllegalStateException("Book is required") } - if (props.versionId.value == null) { + if (props.versionId == null) { throw IllegalStateException("Version is required") } - val start = props.verseStart.value - val end = props.verseEnd.value + val start = props.verseStart + val end = props.verseEnd if (start != null && end != null) { return BibleReference( - versionId = props.versionId.value!!, - bookUSFM = props.bookUSFM.value!!, - chapter = props.chapter.value!!, + versionId = props.versionId, + bookUSFM = props.bookUSFM, + chapter = props.chapter, verseStart = start, verseEnd = end ) } return BibleReference( - versionId = props.versionId.value!!, - bookUSFM = props.bookUSFM.value!!, - chapter = props.chapter.value!!, - verse = props.verse.value + versionId = props.versionId, + bookUSFM = props.bookUSFM, + chapter = props.chapter, + verse = props.verse ) } @Composable fun isDark(props: BibleWidgetViewProps): Boolean { - return when (props.colorScheme.value) { + return when (props.colorScheme) { "dark" -> true "light" -> false else -> isSystemInDarkTheme() diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt index bf4aa8d..14037db 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPSignInWithYouVersionButton.kt @@ -2,8 +2,6 @@ package com.youversion.reactnativesdk.views import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.graphics.Shape import com.youversion.platform.ui.views.SignInWithYouVersionButton import com.youversion.platform.ui.views.SignInWithYouVersionButtonDefaults @@ -11,10 +9,10 @@ import com.youversion.platform.ui.views.SignInWithYouVersionButtonMode import expo.modules.kotlin.views.ComposeProps data class SignInWithYouVersionButtonProps( - val mode: MutableState = mutableStateOf("full"), - val shape: MutableState = mutableStateOf("capsule"), - val isStroked: MutableState = mutableStateOf(true), - val colorScheme: MutableState = mutableStateOf(null) + val mode: String? = "full", + val shape: String? = "capsule", + val isStroked: Boolean? = true, + val colorScheme: String? = null ) : ComposeProps @@ -33,7 +31,7 @@ fun YVPSignInWithYouVersionButton( } fun mode(props: SignInWithYouVersionButtonProps): SignInWithYouVersionButtonMode { - return when (props.mode.value) { + return when (props.mode) { "full" -> SignInWithYouVersionButtonMode.FULL "compact" -> SignInWithYouVersionButtonMode.COMPACT "iconOnly" -> SignInWithYouVersionButtonMode.ICON_ONLY @@ -42,12 +40,12 @@ fun mode(props: SignInWithYouVersionButtonProps): SignInWithYouVersionButtonMode } fun stroked(props: SignInWithYouVersionButtonProps): Boolean { - return props.isStroked.value ?: true + return props.isStroked ?: true } @Composable fun shape(props: SignInWithYouVersionButtonProps): Shape { - return when (props.shape.value) { + return when (props.shape) { "capsule" -> SignInWithYouVersionButtonDefaults.capsuleShape "rectangle" -> SignInWithYouVersionButtonDefaults.rectangleShape else -> SignInWithYouVersionButtonDefaults.capsuleShape @@ -56,7 +54,7 @@ fun shape(props: SignInWithYouVersionButtonProps): Shape { @Composable fun isDark(props: SignInWithYouVersionButtonProps): Boolean { - return when (props.colorScheme.value) { + return when (props.colorScheme) { "dark" -> true "light" -> false else -> isSystemInDarkTheme() diff --git a/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt b/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt index 5450513..3abeadc 100644 --- a/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt +++ b/android/src/main/java/com/youversion/reactnativesdk/views/YVPVotdView.kt @@ -2,30 +2,28 @@ package com.youversion.reactnativesdk.views import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import com.youversion.platform.ui.views.votd.CompactVerseOfTheDay import com.youversion.platform.ui.views.votd.VerseOfTheDay import expo.modules.kotlin.views.ComposeProps data class VotdViewProps( - val bibleVersionId: MutableState = mutableStateOf(3034), - val colorScheme: MutableState = mutableStateOf(null), - val showIcon: MutableState = mutableStateOf(null), - val isCompact: MutableState = mutableStateOf(null) + val bibleVersionId: Int? = 3034, + val colorScheme: String? = null, + val showIcon: Boolean? = null, + val isCompact: Boolean? = null ) : ComposeProps @Composable fun YVPVotdView(props: VotdViewProps, onSharePress: () -> Unit, onFullChapterPress: () -> Unit) { - if (props.isCompact.value == true) { + if (props.isCompact == true) { CompactVerseOfTheDay( - bibleVersionId = props.bibleVersionId.value ?: 3034, + bibleVersionId = props.bibleVersionId ?: 3034, dark = isDark(props), showIcon = isIconVisible(props) ) } else { VerseOfTheDay( - bibleVersionId = props.bibleVersionId.value ?: 3034, + bibleVersionId = props.bibleVersionId ?: 3034, dark = isDark(props), onShareClick = { onSharePress() }, onFullChapterClick = { onFullChapterPress() }, @@ -36,7 +34,7 @@ fun YVPVotdView(props: VotdViewProps, onSharePress: () -> Unit, onFullChapterPre @Composable fun isDark(props: VotdViewProps): Boolean { - return when (props.colorScheme.value) { + return when (props.colorScheme) { "dark" -> true "light" -> false else -> isSystemInDarkTheme() @@ -44,5 +42,5 @@ fun isDark(props: VotdViewProps): Boolean { } fun isIconVisible(props: VotdViewProps): Boolean { - return props.showIcon.value ?: true + return props.showIcon ?: true } From 483bef506d128c21097a47f67892d4df8f6ba424 Mon Sep 17 00:00:00 2001 From: Andrew Sidorchuk Date: Mon, 6 Apr 2026 21:09:29 -0400 Subject: [PATCH 15/15] chore: make urlScheme and footnotes optional --- src/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index bd15411..2721f18 100644 --- a/src/types.ts +++ b/src/types.ts @@ -140,9 +140,9 @@ export type BibleReference = export interface OnBibleTextPressEvent { /** A reference to the Bible verse that was pressed */ bibleReference: BibleReferenceVerse; - urlScheme: string; + urlScheme?: string; /** Not implemented yet */ - footnotes: unknown[]; + footnotes?: unknown[]; } export interface LanguageOverview {