Skip to content

Commit 2896dee

Browse files
authored
Add font support to presentPaywall and presentPaywallIfNeeded (#948)
Depends on RevenueCat/purchases-hybrid-common#769 and https://github.com/RevenueCat/purchases-android/pull/1650/files Similar to #916 but with presentPaywall functions
1 parent 9372ca2 commit 2896dee

File tree

5 files changed

+133
-40
lines changed

5 files changed

+133
-40
lines changed

apitesters/paywalls.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ async function checkPresentPaywall(offering: PurchasesOffering) {
3030
offering: offering,
3131
displayCloseButton: false,
3232
});
33+
paywallResult = await RevenueCatUI.presentPaywall({
34+
offering: offering,
35+
displayCloseButton: false,
36+
fontFamily: 'Ubuntu',
37+
});
3338
}
3439

3540
async function checkPresentPaywallIfNeeded(offering: PurchasesOffering) {
@@ -51,6 +56,12 @@ async function checkPresentPaywallIfNeeded(offering: PurchasesOffering) {
5156
offering: offering,
5257
displayCloseButton: false,
5358
});
59+
paywallResult = await RevenueCatUI.presentPaywallIfNeeded({
60+
requiredEntitlementIdentifier: "entitlement",
61+
offering: offering,
62+
displayCloseButton: false,
63+
fontFamily: 'Ubuntu',
64+
});
5465
}
5566

5667
function checkPresentPaywallParams(params: PresentPaywallIfNeededParams) {

react-native-purchases-ui/android/src/main/java/com/revenuecat/purchases/react/ui/FontAssetManager.kt

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ import androidx.compose.ui.text.font.Font
55
import androidx.compose.ui.text.font.FontFamily
66
import androidx.compose.ui.text.font.FontStyle
77
import androidx.compose.ui.text.font.FontWeight
8+
import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI
9+
import com.revenuecat.purchases.ui.revenuecatui.fonts.PaywallFont
10+
import com.revenuecat.purchases.ui.revenuecatui.fonts.PaywallFontFamily
811

912

13+
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
1014
internal object FontAssetManager {
1115
@get:Synchronized
1216
private var fontFamilyCache = mapOf<String, FontFamily>()
17+
private var paywallFontFamilyCache = mapOf<String, PaywallFontFamily>()
1318
private val FILE_EXTENSIONS = arrayOf(".ttf", ".otf")
1419

1520
private const val FONT_PATH = "fonts/"
@@ -39,23 +44,16 @@ internal object FontAssetManager {
3944
if (cachedFontFamily != null) {
4045
return cachedFontFamily
4146
}
42-
val existingFontFileNames = assetManager.list(FONT_PATH)?.toList() ?: emptyList()
43-
val fontsInFamily = mutableListOf<Font>()
44-
FontStyleExtension.values().forEach { styleExtension ->
45-
FILE_EXTENSIONS.forEach { fileNameExtension ->
46-
val fileName = "$fontFamilyName${styleExtension.extension}$fileNameExtension"
47-
if (existingFontFileNames.contains(fileName)) {
48-
fontsInFamily.add(
49-
Font(
50-
path = FONT_PATH + fileName,
51-
assetManager = assetManager,
52-
weight = styleExtension.weight,
53-
style = styleExtension.style
54-
)
55-
)
56-
}
47+
48+
val fontsInFamily =
49+
getFontsInFamily(fontFamilyName, assetManager) { fileName, styleExtension ->
50+
Font(
51+
path = FONT_PATH + fileName,
52+
assetManager = assetManager,
53+
weight = styleExtension.weight,
54+
style = styleExtension.style
55+
)
5756
}
58-
}
5957

6058
return if (fontsInFamily.isNotEmpty()) {
6159
val fontFamily = FontFamily(fontsInFamily)
@@ -65,4 +63,50 @@ internal object FontAssetManager {
6563
null
6664
}
6765
}
66+
67+
@Synchronized
68+
fun getPaywallFontFamily(
69+
fontFamilyName: String,
70+
assetManager: AssetManager
71+
): PaywallFontFamily? {
72+
val cachedPaywallFontFamily = paywallFontFamilyCache[fontFamilyName]
73+
if (cachedPaywallFontFamily != null) {
74+
return cachedPaywallFontFamily
75+
}
76+
77+
val paywallFontsInFamily =
78+
getFontsInFamily(fontFamilyName, assetManager) { fileName, styleExtension ->
79+
PaywallFont.AssetFont(
80+
path = FONT_PATH + fileName,
81+
fontWeight = styleExtension.weight,
82+
fontStyle = styleExtension.style.value
83+
)
84+
}
85+
86+
return if (paywallFontsInFamily.isNotEmpty()) {
87+
val fontFamily = PaywallFontFamily(paywallFontsInFamily)
88+
paywallFontFamilyCache = paywallFontFamilyCache + (fontFamilyName to fontFamily)
89+
fontFamily
90+
} else {
91+
null
92+
}
93+
}
94+
95+
private fun <T> getFontsInFamily(
96+
fontFamilyName: String,
97+
assetManager: AssetManager,
98+
createFont: (String, FontStyleExtension) -> T
99+
): List<T> {
100+
val existingFontFileNames = assetManager.list(FONT_PATH)?.toList() ?: emptyList()
101+
val fontsInFamily = mutableListOf<T>()
102+
FontStyleExtension.values().forEach { styleExtension ->
103+
FILE_EXTENSIONS.forEach { fileNameExtension ->
104+
val fileName = "$fontFamilyName${styleExtension.extension}$fileNameExtension"
105+
if (existingFontFileNames.contains(fileName)) {
106+
fontsInFamily.add(createFont(fileName, styleExtension))
107+
}
108+
}
109+
}
110+
return fontsInFamily
111+
}
68112
}

react-native-purchases-ui/android/src/main/java/com/revenuecat/purchases/react/ui/RNPaywallsModule.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import com.revenuecat.purchases.hybridcommon.ui.PaywallResultListener
1010
import com.revenuecat.purchases.hybridcommon.ui.PaywallSource
1111
import com.revenuecat.purchases.hybridcommon.ui.PresentPaywallOptions
1212
import com.revenuecat.purchases.hybridcommon.ui.presentPaywallFromFragment
13+
import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI
14+
import com.revenuecat.purchases.ui.revenuecatui.fonts.PaywallFont
15+
import com.revenuecat.purchases.ui.revenuecatui.fonts.PaywallFontFamily
1316

1417
internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
1518
ReactContextBaseJavaModule(reactContext) {
@@ -36,12 +39,14 @@ internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
3639
fun presentPaywall(
3740
offeringIdentifier: String?,
3841
displayCloseButton: Boolean?,
42+
fontFamily: String?,
3943
promise: Promise
4044
) {
4145
presentPaywall(
4246
null,
4347
offeringIdentifier,
4448
displayCloseButton,
49+
fontFamily,
4550
promise
4651
)
4752
}
@@ -51,12 +56,14 @@ internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
5156
requiredEntitlementIdentifier: String,
5257
offeringIdentifier: String?,
5358
displayCloseButton: Boolean,
59+
fontFamily: String?,
5460
promise: Promise
5561
) {
5662
presentPaywall(
5763
requiredEntitlementIdentifier,
5864
offeringIdentifier,
5965
displayCloseButton,
66+
fontFamily,
6067
promise
6168
)
6269
}
@@ -71,14 +78,18 @@ internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
7178
// Keep: Required for RN built in Event Emitter Calls.
7279
}
7380

81+
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
7482
private fun presentPaywall(
7583
requiredEntitlementIdentifier: String?,
7684
offeringIdentifier: String?,
7785
displayCloseButton: Boolean?,
86+
fontFamilyName: String?,
7887
promise: Promise
7988
) {
8089
val fragment = currentActivityFragment ?: return
81-
90+
val fontFamily = fontFamilyName?.let {
91+
FontAssetManager.getPaywallFontFamily(fontFamilyName = it, fragment.resources.assets)
92+
}
8293
presentPaywallFromFragment(
8394
fragment = fragment,
8495
PresentPaywallOptions(
@@ -91,7 +102,8 @@ internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
91102
override fun onPaywallResult(paywallResult: String) {
92103
promise.resolve(paywallResult)
93104
}
94-
}
105+
},
106+
fontFamily = fontFamily
95107
)
96108
)
97109
}

react-native-purchases-ui/ios/RNPaywalls.m

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,21 @@ - (PaywallProxy *)paywalls API_AVAILABLE(ios(15.0)){
6363

6464
RCT_EXPORT_METHOD(presentPaywall:(nullable NSString *)offeringIdentifier
6565
shouldDisplayCloseButton:(BOOL)displayCloseButton
66+
withFontFamily:(nullable NSString *)fontFamily
6667
withResolve:(RCTPromiseResolveBlock)resolve
6768
reject:(RCTPromiseRejectBlock)reject) {
6869
if (@available(iOS 15.0, *)) {
70+
NSMutableDictionary *options = [NSMutableDictionary dictionary];
6971
if (offeringIdentifier != nil) {
70-
[self.paywalls presentPaywallWithOfferingIdentifier:offeringIdentifier
71-
displayCloseButton:displayCloseButton
72-
paywallResultHandler:^(NSString *result) {
73-
resolve(result);
74-
}];
75-
return;
72+
options[PaywallOptionsKeys.offeringIdentifier] = offeringIdentifier;
7673
}
77-
[self.paywalls presentPaywallWithDisplayCloseButton:displayCloseButton
78-
paywallResultHandler:^(NSString *result) {
74+
options[PaywallOptionsKeys.displayCloseButton] = @(displayCloseButton);
75+
if (fontFamily) {
76+
options[PaywallOptionsKeys.fontName] = fontFamily;
77+
}
78+
79+
[self.paywalls presentPaywallWithOptions:options
80+
paywallResultHandler:^(NSString *result) {
7981
resolve(result);
8082
}];
8183
} else {
@@ -86,21 +88,22 @@ - (PaywallProxy *)paywalls API_AVAILABLE(ios(15.0)){
8688
RCT_EXPORT_METHOD(presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifier
8789
withOfferingIdentifier:(nullable NSString *)offeringIdentifier
8890
shouldDisplayCloseButton:(BOOL)displayCloseButton
91+
withFontFamily:(nullable NSString *)fontFamily
8992
withResolve:(RCTPromiseResolveBlock)resolve
9093
reject:(RCTPromiseRejectBlock)reject) {
9194
if (@available(iOS 15.0, *)) {
95+
NSMutableDictionary *options = [NSMutableDictionary dictionary];
9296
if (offeringIdentifier != nil) {
93-
[self.paywalls presentPaywallIfNeededWithRequiredEntitlementIdentifier:requiredEntitlementIdentifier
94-
offeringIdentifier:offeringIdentifier
95-
displayCloseButton:displayCloseButton
96-
paywallResultHandler:^(NSString *result) {
97-
resolve(result);
98-
}];
99-
return;
97+
options[PaywallOptionsKeys.offeringIdentifier] = offeringIdentifier;
10098
}
101-
[self.paywalls presentPaywallIfNeededWithRequiredEntitlementIdentifier:requiredEntitlementIdentifier
102-
displayCloseButton:displayCloseButton
103-
paywallResultHandler:^(NSString *result) {
99+
options[PaywallOptionsKeys.requiredEntitlementIdentifier] = requiredEntitlementIdentifier;
100+
options[PaywallOptionsKeys.displayCloseButton] = @(displayCloseButton);
101+
if (fontFamily) {
102+
options[PaywallOptionsKeys.fontName] = fontFamily;
103+
}
104+
105+
[self.paywalls presentPaywallIfNeededWithOptions:options
106+
paywallResultHandler:^(NSString *result) {
104107
resolve(result);
105108
}];
106109
} else {

react-native-purchases-ui/src/index.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ export interface PresentPaywallParams {
5656
* The offering to load the paywall with. This will be the "current" offering by default.
5757
*/
5858
offering?: PurchasesOffering;
59+
60+
/**
61+
* The fontFamily name to use in the Paywall. In order to add a font family, add it in the react native app and make
62+
* sure to run `npx react-native-asset` so it's added to the native components.
63+
* Supported font types are `.ttf` and `.otf`.
64+
* Make sure the file names follow the convention:
65+
* - Regular: MyFont.ttf/MyFont.otf
66+
* - Bold: MyFont_bold.ttf/MyFont_bold.otf
67+
* - Italic: MyFont_italic.ttf/MyFont_italic.otf
68+
* - Bold and Italic: MyFont_bold_italic.ttf/MyFont_bold_italic.otf
69+
*/
70+
fontFamily?: string | null;
5971
}
6072

6173
export type PresentPaywallIfNeededParams = PresentPaywallParams & {
@@ -152,9 +164,14 @@ export default class RevenueCatUI {
152164
*/
153165
public static presentPaywall({
154166
offering,
155-
displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON
167+
displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON,
168+
fontFamily,
156169
}: PresentPaywallParams = {}): Promise<PAYWALL_RESULT> {
157-
return RNPaywalls.presentPaywall(offering?.identifier ?? null, displayCloseButton)
170+
return RNPaywalls.presentPaywall(
171+
offering?.identifier ?? null,
172+
displayCloseButton,
173+
fontFamily,
174+
)
158175
}
159176

160177
/**
@@ -173,9 +190,15 @@ export default class RevenueCatUI {
173190
public static presentPaywallIfNeeded({
174191
requiredEntitlementIdentifier,
175192
offering,
176-
displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON
193+
displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON,
194+
fontFamily,
177195
}: PresentPaywallIfNeededParams): Promise<PAYWALL_RESULT> {
178-
return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier, offering?.identifier ?? null, displayCloseButton)
196+
return RNPaywalls.presentPaywallIfNeeded(
197+
requiredEntitlementIdentifier,
198+
offering?.identifier ?? null,
199+
displayCloseButton,
200+
fontFamily,
201+
)
179202
}
180203

181204
public static Paywall: React.FC<FullScreenPaywallViewProps> = ({

0 commit comments

Comments
 (0)