This commit is contained in:
Jamie Brunton committed 2025-12-29 21:03:25 +00:00
1 parent d1ca114285
commit 8acc43981e
22 files changed
+1731 -97

No files matched your search

+40 -14
View File
@@ -73,18 +73,21 @@ class RevenueCatService {
return;
}
const available = await this.loadPurchases();
if (!available) return;
if (!REVENUECAT_IOS_API_KEY) {
console.warn("RevenueCat: Missing iOS API key");
// Skip if API key is missing or placeholder
if (!REVENUECAT_IOS_API_KEY || REVENUECAT_IOS_API_KEY === "your_revenuecat_ios_api_key") {
console.log("RevenueCat: Skipping - no valid API key configured");
return;
}
const available = await this.loadPurchases();
if (!available) return;
try {
if (__DEV__) {
this.Purchases.setLogLevel(this.Purchases.LOG_LEVEL?.DEBUG || 4);
}
// Always enable debug logging for now to diagnose issues
this.Purchases.setLogLevel(this.Purchases.LOG_LEVEL?.DEBUG || 4);
const keyPrefix = REVENUECAT_IOS_API_KEY.substring(0, 10);
console.log("RevenueCat: Configuring with API key prefix:", keyPrefix + "...");
await this.Purchases.configure({
apiKey: REVENUECAT_IOS_API_KEY,
@@ -92,9 +95,10 @@ class RevenueCatService {
});
this.initialized = true;
console.log("RevenueCat: Initialized successfully");
console.log("RevenueCat: Initialized successfully with user:", userId || "anonymous");
} catch (error) {
console.error("RevenueCat: Failed to initialize:", error);
// Don't rethrow - allow app to continue without subscriptions
}
}
@@ -129,16 +133,38 @@ class RevenueCatService {
}
async getOfferings(): Promise<PurchasesOffering | null> {
const result = await this.getOfferingsDebug();
return result.current;
}
async getOfferingsDebug(): Promise<{
current: PurchasesOffering | null;
availableOfferings: string[] | null;
error: string | null;
}> {
if (!this.initialized || !this.Purchases) {
return null;
return {
current: null,
availableOfferings: null,
error: "RevenueCat not initialized"
};
}
try {
const offerings = await this.Purchases.getOfferings();
return offerings.current;
} catch (error) {
console.error("RevenueCat: Error fetching offerings:", error);
return null;
const availableOfferings = Object.keys(offerings.all || {});
return {
current: offerings.current,
availableOfferings,
error: null
};
} catch (error: any) {
return {
current: null,
availableOfferings: null,
error: error.message || "Failed to fetch offerings"
};
}
}
+75
View File
@@ -0,0 +1,75 @@
import * as SecureStore from "expo-secure-store";
import { Platform } from "react-native";
const STORAGE_KEY = "skipped_questions";
const SKIP_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours
interface SkippedQuestion {
id: string;
skippedAt: number;
}
async function getStorage(): Promise<SkippedQuestion[]> {
try {
let data: string | null = null;
if (Platform.OS === "web") {
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
data = window.localStorage.getItem(STORAGE_KEY);
}
} else {
data = await SecureStore.getItemAsync(STORAGE_KEY);
}
if (data) {
return JSON.parse(data);
}
} catch (error) {
console.error("Error reading skipped questions:", error);
}
return [];
}
async function setStorage(questions: SkippedQuestion[]): Promise<void> {
try {
const data = JSON.stringify(questions);
if (Platform.OS === "web") {
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
window.localStorage.setItem(STORAGE_KEY, data);
}
} else {
await SecureStore.setItemAsync(STORAGE_KEY, data);
}
} catch (error) {
console.error("Error saving skipped questions:", error);
}
}
export async function skipQuestion(questionId: string): Promise<void> {
const skipped = await getStorage();
const now = Date.now();
// Remove old entries (older than SKIP_DURATION_MS)
const filtered = skipped.filter(q => now - q.skippedAt < SKIP_DURATION_MS);
// Add new skipped question (or update timestamp if already exists)
const existingIndex = filtered.findIndex(q => q.id === questionId);
if (existingIndex >= 0) {
filtered[existingIndex].skippedAt = now;
} else {
filtered.push({ id: questionId, skippedAt: now });
}
await setStorage(filtered);
}
export async function getSkippedQuestionIds(): Promise<Set<string>> {
const skipped = await getStorage();
const now = Date.now();
// Return only question IDs that are still within the skip duration
const validSkipped = skipped.filter(q => now - q.skippedAt < SKIP_DURATION_MS);
return new Set(validSkipped.map(q => q.id));
}