Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
node-version: '22.x'
registry-url: 'https://registry.npmjs.org'

- name: Upgrade npm
run: npm install -g [email protected]

- name: Build
run: |
yarn
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
dependencies {
implementation "com.facebook.react:react-android"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "io.qonversion:sandwich:7.3.1"
implementation "io.qonversion:sandwich:7.4.0"
}

if (isNewArchitectureEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ class NoCodesModule(private val reactContext: ReactApplicationContext) : NativeN
}

@ReactMethod
override fun initialize(projectKey: String, source: String, version: String, proxyUrl: String?, locale: String?) {
override fun initialize(projectKey: String, source: String, version: String, proxyUrl: String?, locale: String?, theme: String?) {
noCodesSandwich.storeSdkInfo(reactContext, source, version)
noCodesSandwich.initialize(reactContext, projectKey, null, null, proxyUrl, locale)
noCodesSandwich.initialize(reactContext, projectKey, null, null, proxyUrl, locale, theme)
noCodesSandwich.setDelegate(noCodesEventListener)
noCodesSandwich.setScreenCustomizationDelegate()
}
Expand Down Expand Up @@ -111,6 +111,11 @@ class NoCodesModule(private val reactContext: ReactApplicationContext) : NativeN
noCodesSandwich.setLocale(locale)
}

@ReactMethod
override fun setTheme(theme: String?) {
noCodesSandwich.setTheme(theme)
}

companion object {
const val NAME = "RNNoCodes"
}
Expand Down
20 changes: 10 additions & 10 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ PODS:
- hermes-engine (0.80.1):
- hermes-engine/Pre-built (= 0.80.1)
- hermes-engine/Pre-built (0.80.1)
- Qonversion (6.3.0):
- Qonversion/Main (= 6.3.0)
- qonversion-react-native-sdk (10.0.2):
- Qonversion (6.4.0):
- Qonversion/Main (= 6.4.0)
- qonversion-react-native-sdk (10.1.1):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- QonversionSandwich (= 7.3.0)
- QonversionSandwich (= 7.4.0)
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
Expand All @@ -40,9 +40,9 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- Qonversion/Main (6.3.0)
- QonversionSandwich (7.3.0):
- Qonversion (= 6.3.0)
- Qonversion/Main (6.4.0)
- QonversionSandwich (7.4.0):
- Qonversion (= 6.4.0)
- RCT-Folly (2024.11.18.00):
- boost
- DoubleConversion
Expand Down Expand Up @@ -2437,9 +2437,9 @@ SPEC CHECKSUMS:
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: 4f07404533b808de66cf48ac4200463068d0e95a
Qonversion: 8f8fb604e2b9fb442eeb6a7aefcbc6bd73c3b55a
qonversion-react-native-sdk: a11f2ab3bb7f7a89d8c16523e8b7b90985358455
QonversionSandwich: 046f2896c41037f3f58df63e4f22bd351312eeef
Qonversion: f7620e1cc3b03541b9cf4a0ed357a5646d123fe3
qonversion-react-native-sdk: a0520e74e9840f14b110f806dd1e7cbd3d093fae
QonversionSandwich: 58a58f43664d2ddb755b315b51adfe9392ede3f9
RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f
RCTDeprecation: efa5010912100e944a7ac9a93a157e1def1988fe
RCTRequired: bbc4cf999ddc4a4b076e076c74dd1d39d0254630
Expand Down
35 changes: 35 additions & 0 deletions example/src/screens/NoCodesScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Qonversion, {
ScreenPresentationConfig,
NoCodes,
NoCodesError,
NoCodesTheme,
type PurchaseDelegate,
Product
} from '@qonversion/react-native-sdk';
Expand All @@ -35,6 +36,7 @@ const NoCodesScreen: React.FC = () => {
);
const [animated, setAnimated] = useState(false);
const [locale, setLocale] = useState('');
const [theme, setTheme] = useState<NoCodesTheme>(NoCodesTheme.AUTO);

useEffect(() => {
// Initialize No-Codes SDK once
Expand Down Expand Up @@ -216,6 +218,22 @@ const NoCodesScreen: React.FC = () => {
}
};

const applyTheme = (selectedTheme: NoCodesTheme) => {
try {
console.log('🔄 [NoCodes] Setting theme to:', selectedTheme);
setTheme(selectedTheme);
NoCodes.getSharedInstance().setTheme(selectedTheme);
console.log('✅ [NoCodes] setTheme() call successful');
Snackbar.show({
text: `Theme set to: ${selectedTheme}`,
duration: Snackbar.LENGTH_SHORT,
});
} catch (error: any) {
console.error('❌ [NoCodes] setTheme() call failed:', error);
Alert.alert('Error', error.message);
}
};

return (
<ScrollView
style={styles.container}
Expand Down Expand Up @@ -283,6 +301,23 @@ const NoCodesScreen: React.FC = () => {
</TouchableOpacity>
</View>

<View style={styles.inputContainer}>
<Text style={styles.sectionTitle}>Theme</Text>
<Text style={styles.inputLabel}>Select theme mode:</Text>
{Object.values(NoCodesTheme).map((themeOption) => (
<TouchableOpacity
key={themeOption}
style={[
styles.radioButton,
theme === themeOption && styles.radioButtonSelected,
]}
onPress={() => applyTheme(themeOption)}
>
<Text style={styles.radioButtonText}>{themeOption}</Text>
</TouchableOpacity>
))}
</View>

<TouchableOpacity style={styles.button} onPress={close}>
<Text style={styles.buttonText}>Close</Text>
</TouchableOpacity>
Expand Down
9 changes: 7 additions & 2 deletions ios/RNNoCodes.mm
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ - (void)initialize:(NSString *)projectKey
source:(NSString *)source
version:(NSString *)version
proxyUrl:(NSString *)proxyUrl
locale:(NSString *)locale {
[self.impl initializeWithProjectKey:projectKey source:source version:version proxyUrl:proxyUrl locale:locale];
locale:(NSString *)locale
theme:(NSString *)theme {
[self.impl initializeWithProjectKey:projectKey source:source version:version proxyUrl:proxyUrl locale:locale theme:theme];
}

- (void)setScreenPresentationConfig:(NSDictionary *)configData
Expand Down Expand Up @@ -76,6 +77,10 @@ - (void)setLocale:(NSString *)locale {
[self.impl setLocale:locale];
}

- (void)setTheme:(NSString *)theme {
[self.impl setTheme:theme];
}

#pragma mark - NoCodesEventDelegate

- (void)noCodesDidTriggerWithEvent:(NSString * _Nonnull)event payload:(NSDictionary<NSString *,id> * _Nullable)payload {
Expand Down
9 changes: 7 additions & 2 deletions ios/RNNoCodesImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ public class RNNoCodesImpl: NSObject {
}

@objc
public func initialize(projectKey: String, source: String, version: String, proxyUrl: String?, locale: String?) {
public func initialize(projectKey: String, source: String, version: String, proxyUrl: String?, locale: String?, theme: String?) {
// Ignore source and version, because it's taken from the Qonversion SDK.
noCodesSandwich?.initialize(projectKey: projectKey, proxyUrl: proxyUrl, locale: locale)
noCodesSandwich?.initialize(projectKey: projectKey, proxyUrl: proxyUrl, locale: locale, theme: theme)
}

@MainActor @objc
Expand Down Expand Up @@ -121,4 +121,9 @@ public class RNNoCodesImpl: NSObject {
public func setLocale(_ locale: String?) {
noCodesSandwich?.setLocale(locale)
}

@objc
public func setTheme(_ theme: String?) {
noCodesSandwich?.setTheme(theme)
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@qonversion/react-native-sdk",
"title": "React Native Qonversion",
"version": "10.1.1",
"version": "10.2.0",
"description": "Qonversion provides full in-app purchases infrastructure, so you do not need to build your own server for receipt validation. Implement in-app subscriptions, validate user receipts, check subscription status, and provide access to your app features and content using our StoreKit wrapper and Google Play Billing wrapper.",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion qonversion-react-native-sdk.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
s.private_header_files = "ios/**/*.h"

s.dependency "QonversionSandwich", "7.3.1"
s.dependency "QonversionSandwich", "7.4.0"
install_modules_dependencies(s)
end
10 changes: 10 additions & 0 deletions src/NoCodesApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ScreenPresentationConfig from './dto/ScreenPresentationConfig';
import type { NoCodesTheme } from './dto/enums';

export default interface NoCodesApi {
/**
Expand Down Expand Up @@ -28,4 +29,13 @@ export default interface NoCodesApi {
* @param locale the locale to use (e.g. "en", "de", "fr"), or null to reset to device default.
*/
setLocale(locale: string | null): void;

/**
* Set the theme mode for No-Code screens.
* Controls how screens adapt to light/dark themes.
*
* @param theme the desired theme mode. Use {@link NoCodesTheme.AUTO} to follow device settings,
* {@link NoCodesTheme.LIGHT} to force light theme, or {@link NoCodesTheme.DARK} to force dark theme.
*/
setTheme(theme: NoCodesTheme): void;
}
6 changes: 5 additions & 1 deletion src/NoCodesConfig.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import type { NoCodesListener } from './dto/NoCodesListener';
import type { PurchaseDelegate } from './dto/PurchaseDelegate';
import type { NoCodesTheme } from './dto/enums';

class NoCodesConfig {
readonly projectKey: string;
readonly noCodesListener: NoCodesListener | undefined;
readonly purchaseDelegate: PurchaseDelegate | undefined;
readonly proxyUrl: string | undefined;
readonly locale: string | undefined;
readonly theme: NoCodesTheme | undefined;

constructor(
projectKey: string,
noCodesListener: NoCodesListener | undefined = undefined,
purchaseDelegate: PurchaseDelegate | undefined = undefined,
proxyUrl: string | undefined = undefined,
locale: string | undefined = undefined
locale: string | undefined = undefined,
theme: NoCodesTheme | undefined = undefined
) {
this.projectKey = projectKey;
this.noCodesListener = noCodesListener;
this.purchaseDelegate = purchaseDelegate;
this.proxyUrl = proxyUrl;
this.locale = locale;
this.theme = theme;
}
}

Expand Down
18 changes: 17 additions & 1 deletion src/NoCodesConfigBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {NoCodesListener} from './dto/NoCodesListener';
import type {PurchaseDelegate} from './dto/PurchaseDelegate';
import {NoCodesTheme} from './dto/enums';
import NoCodesConfig from './NoCodesConfig';

class NoCodesConfigBuilder {
Expand All @@ -8,6 +9,7 @@ class NoCodesConfigBuilder {
private purchaseDelegate: PurchaseDelegate | undefined = undefined;
private proxyUrl: string | undefined = undefined;
private locale: string | undefined = undefined;
private theme: NoCodesTheme | undefined = undefined;

constructor(projectKey: string) {
this.projectKey = projectKey;
Expand Down Expand Up @@ -63,6 +65,19 @@ class NoCodesConfigBuilder {
return this;
}

/**
* Set the theme mode for No-Code screens.
* Controls how screens adapt to light/dark themes.
*
* @param theme the desired theme mode. Use {@link NoCodesTheme.AUTO} to follow device settings,
* {@link NoCodesTheme.LIGHT} to force light theme, or {@link NoCodesTheme.DARK} to force dark theme.
* @return builder instance for chain calls.
*/
setTheme(theme: NoCodesTheme): NoCodesConfigBuilder {
this.theme = theme;
return this;
}

/**
* Generate {@link NoCodesConfig} instance with all the provided configurations.
*
Expand All @@ -74,7 +89,8 @@ class NoCodesConfigBuilder {
this.noCodesListener,
this.purchaseDelegate,
this.proxyUrl,
this.locale
this.locale,
this.theme
);
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/dto/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,28 @@ export enum QonversionErrorCode {
UNKNOWN_CLIENT_PLATFORM = "UnknownClientPlatform", // The current platform is not supported
}

/**
* Theme mode for No-Code screens.
* Use this to control how screens adapt to light/dark themes.
*/
export enum NoCodesTheme {
/**
* Automatically follow the device's system appearance (default).
* The screen will use light theme in light mode and dark theme in dark mode.
*/
AUTO = 'auto',

/**
* Force light theme regardless of device settings.
*/
LIGHT = 'light',

/**
* Force dark theme regardless of device settings.
*/
DARK = 'dark',
}

export enum NoCodesErrorCode {
UNKNOWN = "Unknown",
BAD_NETWORK_REQUEST = "BadNetworkRequest",
Expand Down
8 changes: 6 additions & 2 deletions src/internal/NoCodesInternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {NoCodesListener} from '../dto/NoCodesListener';
import type {PurchaseDelegate} from '../dto/PurchaseDelegate';
import ScreenPresentationConfig from '../dto/ScreenPresentationConfig';
import NoCodesError from '../dto/NoCodesError';
import {NoCodesErrorCode} from '../dto/enums';
import {NoCodesErrorCode, NoCodesTheme} from '../dto/enums';
import RNNoCodes, {type NoCodeEvent} from './specs/NativeNoCodesModule';
import {sdkSource, sdkVersion} from './QonversionInternal';
import Product from '../dto/Product';
Expand All @@ -22,7 +22,7 @@ export default class NoCodesInternal implements NoCodesApi {
private purchaseDelegate: PurchaseDelegate | null = null;

constructor(config: NoCodesConfig) {
RNNoCodes.initialize(config.projectKey, sdkSource, sdkVersion, config.proxyUrl, config.locale);
RNNoCodes.initialize(config.projectKey, sdkSource, sdkVersion, config.proxyUrl, config.locale, config.theme);

if (config.noCodesListener) {
this.setNoCodesListener(config.noCodesListener);
Expand Down Expand Up @@ -122,4 +122,8 @@ export default class NoCodesInternal implements NoCodesApi {
setLocale(locale: string | null) {
RNNoCodes.setLocale(locale);
}

setTheme(theme: NoCodesTheme) {
RNNoCodes.setTheme(theme);
}
}
2 changes: 1 addition & 1 deletion src/internal/QonversionInternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import PromotionalOffer from '../dto/PromotionalOffer';
import RNQonversion from './specs/NativeQonversionModule';
import type { QPromoOfferDetails } from './specs/NativeQonversionModule';

export const sdkVersion = "10.1.1";
export const sdkVersion = "10.2.0";
export const sdkSource = "rn";

export default class QonversionInternal implements QonversionApi {
Expand Down
3 changes: 2 additions & 1 deletion src/internal/specs/NativeNoCodesModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ export type NoCodeEvent = {
};

export interface Spec extends TurboModule {
initialize(projectKey: string, source: string, version: string, proxyUrl?: string, locale?: string): void;
initialize(projectKey: string, source: string, version: string, proxyUrl?: string, locale?: string, theme?: string): void;
setScreenPresentationConfig(configData: Object, contextKey?: string): Promise<boolean>;
showScreen(contextKey: string): Promise<boolean>;
close(): Promise<boolean>;
setPurchaseDelegate(): void;
setLocale(locale: string | null): void;
setTheme(theme: string): void;

// Methods to notify native code about purchase/restore results
delegatedPurchaseCompleted(): void;
Expand Down