Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [7.5.0](https://github.com/smallcase/react-native-smallcase-gateway/compare/v7.4.4...v7.5.0) (2026-07-10)

### [7.4.4](https://github.com/smallcase/react-native-smallcase-gateway/compare/v7.4.3...v7.4.4) (2026-06-11)

### [7.4.3](https://github.com/smallcase/react-native-smallcase-gateway/compare/v7.4.0...v7.4.3) (2026-06-09)
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+' // From node_modules
implementation 'com.smallcase.gateway:sdk:6.1.2'
implementation 'com.smallcase.loans:sdk:5.1.3'
implementation 'com.smallcase.loans:sdk:5.2.1'
implementation "androidx.core:core-ktx:1.3.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte
promise.reject(Throwable("Interaction token is null"))
return
}
val loanConfigObj = ScLoanInfo(interactionToken)
val loanConfigObj = ScLoanInfo(interactionToken, parseColorScheme(hashMap["colorScheme"]))
ScLoan.apply(appCompatActivity, loanConfigObj, object : ScLoanResult {
override fun onFailure(error: ScLoanError) {
promise.reject("${error.code}", scLoanResponseToWritableMap(error) ?: return)
Expand All @@ -376,7 +376,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte
promise.reject(Throwable("Interaction token is null"))
return
}
val loanConfigObj = ScLoanInfo(interactionToken)
val loanConfigObj = ScLoanInfo(interactionToken, parseColorScheme(hashMap["colorScheme"]))
ScLoan.pay(appCompatActivity, loanConfigObj, object : ScLoanResult {
override fun onFailure(error: ScLoanError) {
promise.reject("${error.code}", scLoanResponseToWritableMap(error) ?: return)
Expand All @@ -400,7 +400,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte
promise.reject(Throwable("Interaction token is null"))
return
}
val loanConfigObj = ScLoanInfo(interactionToken)
val loanConfigObj = ScLoanInfo(interactionToken, parseColorScheme(hashMap["colorScheme"]))
ScLoan.withdraw(appCompatActivity, loanConfigObj, object : ScLoanResult {
override fun onFailure(error: ScLoanError) {
promise.reject("${error.code}", scLoanResponseToWritableMap(error) ?: return)
Expand All @@ -424,7 +424,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte
promise.reject(Throwable("Interaction token is null"))
return
}
val loanConfigObj = ScLoanInfo(interactionToken)
val loanConfigObj = ScLoanInfo(interactionToken, parseColorScheme(hashMap["colorScheme"]))
ScLoan.service(appCompatActivity, loanConfigObj, object : ScLoanResult {
override fun onFailure(error: ScLoanError) {
promise.reject("${error.code}", scLoanResponseToWritableMap(error) ?: return)
Expand All @@ -448,7 +448,7 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte
promise.reject(Throwable("Interaction token is null"))
return
}
val loanConfigObj = ScLoanInfo(interactionToken)
val loanConfigObj = ScLoanInfo(interactionToken, parseColorScheme(hashMap["colorScheme"]))
ScLoan.triggerInteraction(appCompatActivity, loanConfigObj, object : ScLoanResult {
override fun onFailure(error: ScLoanError) {
promise.reject("${error.code}", scLoanResponseToWritableMap(error) ?: return)
Expand All @@ -460,6 +460,16 @@ class SmallcaseGatewayModule(reactContext: ReactApplicationContext) : ReactConte
})
}

// colorScheme rides the bridge as a primitive string (not a serialized object) and is
// mapped back to the native enum here. Unknown/absent → null, so the SDK keeps its
// "partner sent nothing → light, don't force theme param" default.
private fun parseColorScheme(value: String?): ScLoanColorScheme? = when (value) {
"dark" -> ScLoanColorScheme.DARK
"light" -> ScLoanColorScheme.LIGHT
"system" -> ScLoanColorScheme.SYSTEM
else -> null
}

private fun getProtocol(envName: String): Environment.PROTOCOL {
return when (envName) {
"production" -> Environment.PROTOCOL.PRODUCTION
Expand Down
28 changes: 23 additions & 5 deletions ios/SmallcaseGateway.m
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,24 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject)
});
}

// colorScheme rides the bridge as a primitive string (not a serialized object) and is
// mapped back to the native enum here. Unknown/absent → the token-only initializer, so the
// SDK keeps its "partner sent nothing → light, don't force theme param" default.
- (ScLoanInfo *)loanInfoFromDict:(NSDictionary *)loanInfo {
NSString *interactionToken = loanInfo[@"interactionToken"];
id scheme = loanInfo[@"colorScheme"];
if ([scheme isKindOfClass:[NSString class]]) {
if ([scheme isEqualToString:@"dark"]) {
return [[ScLoanInfo alloc] initWithInteractionToken:interactionToken colorScheme:ScLoanColorSchemeDark];
} else if ([scheme isEqualToString:@"light"]) {
return [[ScLoanInfo alloc] initWithInteractionToken:interactionToken colorScheme:ScLoanColorSchemeLight];
} else if ([scheme isEqualToString:@"system"]) {
return [[ScLoanInfo alloc] initWithInteractionToken:interactionToken colorScheme:ScLoanColorSchemeSystem];
}
}
return [[ScLoanInfo alloc] initWithInteractionToken:interactionToken];
}

RCT_REMAP_METHOD(apply,
loanInfo: (NSDictionary *)loanInfo
applyWithResolver:(RCTPromiseResolveBlock)resolve
Expand All @@ -640,7 +658,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject)
NSString *interactionToken = loanInfo[@"interactionToken"];
NSLog(@" ----------- Interaction Token: %@", interactionToken);

ScLoanInfo *gatewayLoanInfo = [[ScLoanInfo alloc] initWithInteractionToken:interactionToken];
ScLoanInfo *gatewayLoanInfo = [self loanInfoFromDict:loanInfo];

[ScLoan.instance applyWithPresentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] loanInfo:gatewayLoanInfo completion:^(ScLoanSuccess * success, ScLoanError * error) {

Expand All @@ -667,7 +685,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject)
NSString *interactionToken = loanInfo[@"interactionToken"];
NSLog(@" ----------- Interaction Token: %@", interactionToken);

ScLoanInfo *gatewayLoanInfo = [[ScLoanInfo alloc] initWithInteractionToken:interactionToken];
ScLoanInfo *gatewayLoanInfo = [self loanInfoFromDict:loanInfo];

[ScLoan.instance payWithPresentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] loanInfo:gatewayLoanInfo completion:^(ScLoanSuccess * success, ScLoanError * error) {

Expand All @@ -694,7 +712,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject)
NSString *interactionToken = loanInfo[@"interactionToken"];
NSLog(@" ----------- Interaction Token: %@", interactionToken);

ScLoanInfo *gatewayLoanInfo = [[ScLoanInfo alloc] initWithInteractionToken:interactionToken];
ScLoanInfo *gatewayLoanInfo = [self loanInfoFromDict:loanInfo];

[ScLoan.instance withdrawWithPresentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] loanInfo:gatewayLoanInfo completion:^(ScLoanSuccess * success, ScLoanError * error) {

Expand All @@ -721,7 +739,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject)
NSString *interactionToken = loanInfo[@"interactionToken"];
NSLog(@" ----------- Interaction Token: %@", interactionToken);

ScLoanInfo *gatewayLoanInfo = [[ScLoanInfo alloc] initWithInteractionToken:interactionToken];
ScLoanInfo *gatewayLoanInfo = [self loanInfoFromDict:loanInfo];

[ScLoan.instance serviceWithPresentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] loanInfo:gatewayLoanInfo completion:^(ScLoanSuccess * success, ScLoanError * error) {

Expand Down Expand Up @@ -749,7 +767,7 @@ @interface RCT_EXTERN_MODULE(SmallcaseGateway, NSObject)
NSString *interactionToken = loanInfo[@"interactionToken"];
NSLog(@" ----------- Interaction Token: %@", interactionToken);

ScLoanInfo *gatewayLoanInfo = [[ScLoanInfo alloc] initWithInteractionToken:interactionToken];
ScLoanInfo *gatewayLoanInfo = [self loanInfoFromDict:loanInfo];

[ScLoan.instance triggerInteractionWithPresentingController:[[[UIApplication sharedApplication] keyWindow] rootViewController] loanInfo:gatewayLoanInfo completion:^(ScLoanSuccess * success, ScLoanError * error) {

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-native-smallcase-gateway",
"title": "React Native Smallcase Gateway",
"version": "7.4.4",
"version": "7.5.0",
"description": "smallcase gateway bindings for react native",
"main": "src/index.js",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion react-native-smallcase-gateway.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ Pod::Spec.new do |s|
end

s.dependency 'SCGateway', '7.2.0'
s.dependency 'SCLoans', '7.1.2'
s.dependency 'SCLoans', '7.3.0'
end
2 changes: 1 addition & 1 deletion smart_investing_react_native/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ react {
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
def enableProguardInReleaseBuilds = false
def enableProguardInReleaseBuilds = true

/**
* The preferred build flavor of JavaScriptCore (JSC)
Expand Down
19 changes: 19 additions & 0 deletions smart_investing_react_native/android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,22 @@
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# SCGateway proguard rules — keeping it here, same as loans.
-keep class com.smallcase.gateway.** { *; }

# Retrofit + R8 full mode (AGP 8 default): generic signatures are stripped for
# any type that is not kept, so ConfigService.getBrokerConfigs(): Call<Foo>
# degrades to a raw Call and Retrofit rejects it
# ("Call return type must be parameterized as Call<Foo>"). These are the
# canonical rules Retrofit 2.9+ bundles in META-INF/proguard/retrofit2.pro; the
# gateway SDK vendors an older Retrofit whose embedded rules never reach the host,
# so we declare them here. allowobfuscation/allowshrinking keep the types
# "kept enough" to retain their generic signatures while still letting R8 rename them.
-keepattributes Signature, InnerClasses, EnclosingMethod
-keepclassmembers,allowshrinking,allowobfuscation interface * {
@retrofit2.http.* <methods>;
}
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
2 changes: 1 addition & 1 deletion smart_investing_react_native/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ hermesEnabled=true

artifactory_contextUrl=https\://artifactory.smallcase.com/artifactory
artifactory_password=reactNativeUser123
artifactory_user=react_native_user
artifactory_user=react_native_user
57 changes: 42 additions & 15 deletions smart_investing_react_native/app/screens/LoansScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,23 @@ import {
import {SIJsonViewer} from '../components/SIJsonViewer';
import {SmartButton} from './HoldingsScreen';
import {SIDropDown} from '../components/SIDropdown';
import SegmentedControl from '@react-native-segmented-control/segmented-control';
import {ScLoan} from 'react-native-smallcase-gateway';

type LoanSummaryType = Awaited<ReturnType<typeof getUnityUserLoanSummary>>;

// Color scheme passed to the LAS web UI. Index 0 ("None") omits colorScheme
// entirely so the SDK keeps its "partner sent nothing → light" default; the
// rest map to the primitive string the native bridge expects.
type ScLoanColorScheme = 'light' | 'dark' | 'system';
const COLOR_SCHEME_LABELS = ['None', 'Light', 'Dark', 'System'];
const COLOR_SCHEME_VALUES: (ScLoanColorScheme | undefined)[] = [
undefined,
'light',
'dark',
'system',
];

export const basePayload = {
intent: 'LOAN_APPLICATION',
config: {
Expand Down Expand Up @@ -63,6 +76,16 @@ const LoansScreen = ({route}: {route: any}) => {
const [loanSummary, setLoanSummary] = useState<LoanSummaryType>();
const [offersInput] = useState<string>('');

// Selected LAS color scheme; defaults to index 0 ("None").
const [colorSchemeIndex, setColorSchemeIndex] = useState<number>(0);

// Single source of truth for the ScLoanInfo passed to every trigger. When the
// selected scheme is "None" we omit the key so the SDK applies its default.
const buildLoanInfo = (token: string) => {
const colorScheme = COLOR_SCHEME_VALUES[colorSchemeIndex];
return colorScheme ? {interactionToken: token, colorScheme} : {interactionToken: token};
};

const [createUnityUserPayload, setCreateUnityUserPayload] =
useState<GenerateUnityUserPayload>({
id: '',
Expand Down Expand Up @@ -227,9 +250,7 @@ const LoansScreen = ({route}: {route: any}) => {
if (typeof interactionToken !== 'string') {
throw new Error('Invalid interaction token!');
}
const applyRes = await ScLoan.apply({
interactionToken: interactionToken,
});
const applyRes = await ScLoan.apply(buildLoanInfo(interactionToken));
alert('Success', `${JSON.stringify(applyRes)}`);
} catch (error: any) {
alert('Error', `${error}, ${JSON.stringify(error.userInfo)}`);
Expand All @@ -241,9 +262,7 @@ const LoansScreen = ({route}: {route: any}) => {
if (typeof interactionToken !== 'string') {
throw new Error('Invalid interaction token!');
}
const payRes = await ScLoan.pay({
interactionToken: interactionToken,
});
const payRes = await ScLoan.pay(buildLoanInfo(interactionToken));
alert('Success', `${JSON.stringify(payRes)}`);
} catch (error: any) {
alert('Error', `${error}, ${JSON.stringify(error.userInfo)}`);
Expand All @@ -255,9 +274,7 @@ const LoansScreen = ({route}: {route: any}) => {
if (typeof interactionToken !== 'string') {
throw new Error('Invalid interaction token!');
}
const withdrawRes = await ScLoan.withdraw({
interactionToken: interactionToken,
});
const withdrawRes = await ScLoan.withdraw(buildLoanInfo(interactionToken));
alert('Success', `${JSON.stringify(withdrawRes)}`);
} catch (error: any) {
alert('Error', `${error}, ${JSON.stringify(error.userInfo)}`);
Expand All @@ -269,9 +286,7 @@ const LoansScreen = ({route}: {route: any}) => {
if (typeof interactionToken !== 'string') {
throw new Error('Invalid interaction token!');
}
const serviceRes = await ScLoan.service({
interactionToken: interactionToken,
});
const serviceRes = await ScLoan.service(buildLoanInfo(interactionToken));
alert('Success', `${JSON.stringify(serviceRes)}`);
} catch (error: any) {
alert('Error', `${error}, ${JSON.stringify(error.userInfo)}`);
Expand All @@ -283,9 +298,9 @@ const LoansScreen = ({route}: {route: any}) => {
if (typeof interactionToken !== 'string') {
throw new Error('Invalid interaction token!');
}
const serviceRes = await ScLoan.triggerInteraction({
interactionToken: interactionToken,
});
const serviceRes = await ScLoan.triggerInteraction(
buildLoanInfo(interactionToken),
);
alert('Success', `${JSON.stringify(serviceRes)}`);
} catch (error: any) {
alert('Error', `${error}, ${JSON.stringify(error.userInfo)}`);
Expand Down Expand Up @@ -515,6 +530,18 @@ const LoansScreen = ({route}: {route: any}) => {
}}
title={'Setup'}
/>
<View style={{height: 12, backgroundColor: 'transparent'}} />
<Text style={{fontSize: 14, fontWeight: 'bold', marginBottom: 6}}>
Color Scheme
</Text>
<SegmentedControl
values={COLOR_SCHEME_LABELS}
selectedIndex={colorSchemeIndex}
onChange={event => {
setColorSchemeIndex(event.nativeEvent.selectedSegmentIndex);
}}
/>
<View style={{height: 12, backgroundColor: 'transparent'}} />
<ScButton onPress={applyForLoan} title={'Apply'} />
<ScButton onPress={payAmount} title={'Pay'} />
<ScButton onPress={withdrawAmount} title={'Withdraw'} />
Expand Down
16 changes: 8 additions & 8 deletions smart_investing_react_native/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1410,14 +1410,14 @@ PODS:
- Yoga
- react-native-segmented-control (2.5.7):
- React-Core
- react-native-smallcase-gateway (7.3.6):
- react-native-smallcase-gateway (7.4.3):
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- ReactCommon/turbomodule/core
- SCGateway (= 7.1.7)
- SCLoans (= 7.1.2)
- SCGateway (= 7.2.0)
- SCLoans (= 7.3.0)
- React-NativeModulesApple (0.79.4):
- glog
- hermes-engine
Expand Down Expand Up @@ -1839,8 +1839,8 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- SCGateway (7.1.7)
- SCLoans (7.1.2)
- SCGateway (7.2.0)
- SCLoans (7.3.0)
- SocketRocket (0.7.1)
- Yoga (0.0.0)

Expand Down Expand Up @@ -2128,7 +2128,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: ef2292ca147fa8793305e4693586ad0caf3afad3
react-native-safe-area-context: 562163222d999b79a51577eda2ea8ad2c32b4d06
react-native-segmented-control: bf6e0032726727498e18dd437ae88afcdbc18e99
react-native-smallcase-gateway: 5af44ebaee1d5326befe862f0cf8cb00143f1098
react-native-smallcase-gateway: 12d3338564d2781ee8be7de425265c47646fa7c6
React-NativeModulesApple: da60186ad0aafff031a9bc86b048711d34acc813
React-oscompat: 472a446c740e39ee39cd57cd7bfd32177c763a2b
React-perflogger: bbca3688c62f4f39e972d6e21969c95fe441fb6c
Expand Down Expand Up @@ -2163,8 +2163,8 @@ SPEC CHECKSUMS:
RNCClipboard: 37de6995ef72dc869422879e51a46a520d3f08b3
RNGestureHandler: ebef699ea17e7c0006c1074e1e423ead60ce0121
RNScreens: 3f6e41f9f89cb888a6b4b27566bdfdf7a3bf51ad
SCGateway: a0f1bc86e449c7d440d8661e0936095b6f345179
SCLoans: 4accc0a0a483f3943cc513a50800485201fed4dd
SCGateway: 8089d79c55ecc8e7b581e0bf56fd28a2e3d957d0
SCLoans: 1effcdedc4be32992f5a1a08afa2b812827e5086
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: a6cb833e04fb8c59a012b49fb1d040fcb0cbb633

Expand Down
Loading
Loading